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 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(());
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 typedef struct { // [HGM] atomic
\r
139 int fromX, fromY, toX, toY, radius;
\r
142 static ExplodeInfo explodeInfo;
\r
144 /* Window class names */
\r
145 char szAppName[] = "WinBoard";
\r
146 char szConsoleName[] = "WBConsole";
\r
148 /* Title bar text */
\r
149 char szTitle[] = "WinBoard";
\r
150 char szConsoleTitle[] = "I C S Interaction";
\r
153 char *settingsFileName;
\r
154 Boolean saveSettingsOnExit;
\r
155 char installDir[MSG_SIZ];
\r
156 int errorExitStatus;
\r
158 BoardSize boardSize;
\r
159 Boolean chessProgram;
\r
160 //static int boardX, boardY;
\r
161 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
162 static int squareSize, lineGap, minorSize;
\r
163 static int winW, winH;
\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
165 static int logoHeight = 0;
\r
166 static char messageText[MESSAGE_TEXT_MAX];
\r
167 static int clockTimerEvent = 0;
\r
168 static int loadGameTimerEvent = 0;
\r
169 static int analysisTimerEvent = 0;
\r
170 static DelayedEventCallback delayedTimerCallback;
\r
171 static int delayedTimerEvent = 0;
\r
172 static int buttonCount = 2;
\r
173 char *icsTextMenuString;
\r
175 char *firstChessProgramNames;
\r
176 char *secondChessProgramNames;
\r
178 #define PALETTESIZE 256
\r
180 HINSTANCE hInst; /* current instance */
\r
181 Boolean alwaysOnTop = FALSE;
\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
184 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
186 ColorClass currentColorClass;
\r
188 HWND hCommPort = NULL; /* currently open comm port */
\r
189 static HWND hwndPause; /* pause button */
\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
192 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
193 explodeBrush, /* [HGM] atomic */
\r
194 markerBrush, /* [HGM] markers */
\r
195 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
198 static HPEN gridPen = NULL;
\r
199 static HPEN highlightPen = NULL;
\r
200 static HPEN premovePen = NULL;
\r
201 static NPLOGPALETTE pLogPal;
\r
202 static BOOL paletteChanged = FALSE;
\r
203 static HICON iconWhite, iconBlack, iconCurrent;
\r
204 static int doingSizing = FALSE;
\r
205 static int lastSizing = 0;
\r
206 static int prevStderrPort;
\r
207 static HBITMAP userLogo;
\r
209 static HBITMAP liteBackTexture = NULL;
\r
210 static HBITMAP darkBackTexture = NULL;
\r
211 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
212 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
213 static int backTextureSquareSize = 0;
\r
214 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
216 #if __GNUC__ && !defined(_winmajor)
\r
217 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
219 #if defined(_winmajor)
\r
220 #define oldDialog (_winmajor < 4)
\r
222 #define oldDialog 0
\r
232 int cliWidth, cliHeight;
\r
235 SizeInfo sizeInfo[] =
\r
237 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
238 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
239 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
240 { "petite", 33, 1, 1, 1, 0, 0 },
\r
241 { "slim", 37, 2, 1, 0, 0, 0 },
\r
242 { "small", 40, 2, 1, 0, 0, 0 },
\r
243 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
244 { "middling", 49, 2, 0, 0, 0, 0 },
\r
245 { "average", 54, 2, 0, 0, 0, 0 },
\r
246 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
247 { "medium", 64, 3, 0, 0, 0, 0 },
\r
248 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
249 { "large", 80, 3, 0, 0, 0, 0 },
\r
250 { "big", 87, 3, 0, 0, 0, 0 },
\r
251 { "huge", 95, 3, 0, 0, 0, 0 },
\r
252 { "giant", 108, 3, 0, 0, 0, 0 },
\r
253 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
254 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
255 { NULL, 0, 0, 0, 0, 0, 0 }
\r
258 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
259 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
261 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
262 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
263 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
264 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
265 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
266 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
267 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
268 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
269 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
270 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
271 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
272 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
273 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
274 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
275 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
276 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
277 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
278 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
281 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
290 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
291 #define N_BUTTONS 5
\r
293 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
295 {"<<", IDM_ToStart, NULL, NULL},
\r
296 {"<", IDM_Backward, NULL, NULL},
\r
297 {"P", IDM_Pause, NULL, NULL},
\r
298 {">", IDM_Forward, NULL, NULL},
\r
299 {">>", IDM_ToEnd, NULL, NULL},
\r
302 int tinyLayout = 0, smallLayout = 0;
\r
303 #define MENU_BAR_ITEMS 7
\r
304 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
305 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
306 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
310 MySound sounds[(int)NSoundClasses];
\r
311 MyTextAttribs textAttribs[(int)NColorClasses];
\r
313 MyColorizeAttribs colorizeAttribs[] = {
\r
314 { (COLORREF)0, 0, "Shout Text" },
\r
315 { (COLORREF)0, 0, "SShout/CShout" },
\r
316 { (COLORREF)0, 0, "Channel 1 Text" },
\r
317 { (COLORREF)0, 0, "Channel Text" },
\r
318 { (COLORREF)0, 0, "Kibitz Text" },
\r
319 { (COLORREF)0, 0, "Tell Text" },
\r
320 { (COLORREF)0, 0, "Challenge Text" },
\r
321 { (COLORREF)0, 0, "Request Text" },
\r
322 { (COLORREF)0, 0, "Seek Text" },
\r
323 { (COLORREF)0, 0, "Normal Text" },
\r
324 { (COLORREF)0, 0, "None" }
\r
329 static char *commentTitle;
\r
330 static char *commentText;
\r
331 static int commentIndex;
\r
332 static Boolean editComment = FALSE;
\r
335 char errorTitle[MSG_SIZ];
\r
336 char errorMessage[2*MSG_SIZ];
\r
337 HWND errorDialog = NULL;
\r
338 BOOLEAN moveErrorMessageUp = FALSE;
\r
339 BOOLEAN consoleEcho = TRUE;
\r
340 CHARFORMAT consoleCF;
\r
341 COLORREF consoleBackgroundColor;
\r
343 char *programVersion;
\r
349 typedef int CPKind;
\r
358 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
361 #define INPUT_SOURCE_BUF_SIZE 4096
\r
363 typedef struct _InputSource {
\r
370 char buf[INPUT_SOURCE_BUF_SIZE];
\r
374 InputCallback func;
\r
375 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
379 InputSource *consoleInputSource;
\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
385 VOID ConsoleCreate();
\r
387 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
392 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
394 void ParseIcsTextMenu(char *icsTextMenuString);
\r
395 VOID PopUpMoveDialog(char firstchar);
\r
396 VOID PopUpNameDialog(char firstchar);
\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
401 int GameListOptions();
\r
403 int dummy; // [HGM] for obsolete args
\r
405 HWND hwndMain = NULL; /* root window*/
\r
406 HWND hwndConsole = NULL;
\r
407 HWND commentDialog = NULL;
\r
408 HWND moveHistoryDialog = NULL;
\r
409 HWND evalGraphDialog = NULL;
\r
410 HWND engineOutputDialog = NULL;
\r
411 HWND gameListDialog = NULL;
\r
412 HWND editTagsDialog = NULL;
\r
414 int commentUp = FALSE;
\r
416 WindowPlacement wpMain;
\r
417 WindowPlacement wpConsole;
\r
418 WindowPlacement wpComment;
\r
419 WindowPlacement wpMoveHistory;
\r
420 WindowPlacement wpEvalGraph;
\r
421 WindowPlacement wpEngineOutput;
\r
422 WindowPlacement wpGameList;
\r
423 WindowPlacement wpTags;
\r
425 VOID EngineOptionsPopup(); // [HGM] settings
\r
427 VOID GothicPopUp(char *title, VariantClass variant);
\r
429 * Setting "frozen" should disable all user input other than deleting
\r
430 * the window. We do this while engines are initializing themselves.
\r
432 static int frozen = 0;
\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
439 if (frozen) return;
\r
441 hmenu = GetMenu(hwndMain);
\r
442 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
443 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
445 DrawMenuBar(hwndMain);
\r
448 /* Undo a FreezeUI */
\r
454 if (!frozen) return;
\r
456 hmenu = GetMenu(hwndMain);
\r
457 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
458 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
460 DrawMenuBar(hwndMain);
\r
463 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
465 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
471 #define JAWS_ALT_INTERCEPT
\r
472 #define JAWS_KB_NAVIGATION
\r
473 #define JAWS_MENU_ITEMS
\r
474 #define JAWS_SILENCE
\r
475 #define JAWS_REPLAY
\r
477 #define JAWS_COPYRIGHT
\r
478 #define JAWS_DELETE(X) X
\r
479 #define SAYMACHINEMOVE()
\r
483 /*---------------------------------------------------------------------------*\
\r
487 \*---------------------------------------------------------------------------*/
\r
490 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
491 LPSTR lpCmdLine, int nCmdShow)
\r
494 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
495 // INITCOMMONCONTROLSEX ex;
\r
499 LoadLibrary("RICHED32.DLL");
\r
500 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
502 if (!InitApplication(hInstance)) {
\r
505 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
511 // InitCommonControlsEx(&ex);
\r
512 InitCommonControls();
\r
514 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
515 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
516 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
518 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
520 while (GetMessage(&msg, /* message structure */
\r
521 NULL, /* handle of window receiving the message */
\r
522 0, /* lowest message to examine */
\r
523 0)) /* highest message to examine */
\r
526 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
527 // [HGM] navigate: switch between all windows with tab
\r
528 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
529 int i, currentElement = 0;
\r
531 // first determine what element of the chain we come from (if any)
\r
532 if(appData.icsActive) {
\r
533 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
534 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
536 if(engineOutputDialog && EngineOutputIsUp()) {
\r
537 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
538 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
540 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
541 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
543 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
544 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
545 if(msg.hwnd == e1) currentElement = 2; else
\r
546 if(msg.hwnd == e2) currentElement = 3; else
\r
547 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
548 if(msg.hwnd == mh) currentElement = 4; else
\r
549 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
550 if(msg.hwnd == hText) currentElement = 5; else
\r
551 if(msg.hwnd == hInput) currentElement = 6; else
\r
552 for (i = 0; i < N_BUTTONS; i++) {
\r
553 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
556 // determine where to go to
\r
557 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
559 currentElement = (currentElement + direction) % 7;
\r
560 switch(currentElement) {
\r
562 h = hwndMain; break; // passing this case always makes the loop exit
\r
564 h = buttonDesc[0].hwnd; break; // could be NULL
\r
566 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
569 if(!EngineOutputIsUp()) continue;
\r
572 if(!MoveHistoryIsUp()) continue;
\r
574 // case 6: // input to eval graph does not seem to get here!
\r
575 // if(!EvalGraphIsUp()) continue;
\r
576 // h = evalGraphDialog; break;
\r
578 if(!appData.icsActive) continue;
\r
582 if(!appData.icsActive) continue;
\r
588 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
589 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
592 continue; // this message now has been processed
\r
596 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
597 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
598 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
599 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
600 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
601 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
602 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
603 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
604 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
605 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
606 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
607 for(i=0; i<MAX_CHAT; i++)
\r
608 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
611 if(done) continue; // [HGM] chat: end patch
\r
612 TranslateMessage(&msg); /* Translates virtual key codes */
\r
613 DispatchMessage(&msg); /* Dispatches message to window */
\r
618 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
621 /*---------------------------------------------------------------------------*\
\r
623 * Initialization functions
\r
625 \*---------------------------------------------------------------------------*/
\r
629 { // update user logo if necessary
\r
630 static char oldUserName[MSG_SIZ], *curName;
\r
632 if(appData.autoLogo) {
\r
633 curName = UserName();
\r
634 if(strcmp(curName, oldUserName)) {
\r
635 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
636 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
637 strcpy(oldUserName, curName);
\r
643 InitApplication(HINSTANCE hInstance)
\r
647 /* Fill in window class structure with parameters that describe the */
\r
650 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
651 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
652 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
653 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
654 wc.hInstance = hInstance; /* Owner of this class */
\r
655 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
656 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
657 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
658 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
659 wc.lpszClassName = szAppName; /* Name to register as */
\r
661 /* Register the window class and return success/failure code. */
\r
662 if (!RegisterClass(&wc)) return FALSE;
\r
664 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
665 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
667 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
668 wc.hInstance = hInstance;
\r
669 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
670 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
671 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
672 wc.lpszMenuName = NULL;
\r
673 wc.lpszClassName = szConsoleName;
\r
675 if (!RegisterClass(&wc)) return FALSE;
\r
680 /* Set by InitInstance, used by EnsureOnScreen */
\r
681 int screenHeight, screenWidth;
\r
684 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
686 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
687 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
688 if (*x > screenWidth - 32) *x = 0;
\r
689 if (*y > screenHeight - 32) *y = 0;
\r
690 if (*x < minX) *x = minX;
\r
691 if (*y < minY) *y = minY;
\r
695 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
697 HWND hwnd; /* Main window handle. */
\r
699 WINDOWPLACEMENT wp;
\r
702 hInst = hInstance; /* Store instance handle in our global variable */
\r
703 programName = szAppName;
\r
705 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
706 *filepart = NULLCHAR;
\r
708 GetCurrentDirectory(MSG_SIZ, installDir);
\r
710 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
711 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
712 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
713 /* xboard, and older WinBoards, controlled the move sound with the
\r
714 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
715 always turn the option on (so that the backend will call us),
\r
716 then let the user turn the sound off by setting it to silence if
\r
717 desired. To accommodate old winboard.ini files saved by old
\r
718 versions of WinBoard, we also turn off the sound if the option
\r
719 was initially set to false. [HGM] taken out of InitAppData */
\r
720 if (!appData.ringBellAfterMoves) {
\r
721 sounds[(int)SoundMove].name = strdup("");
\r
722 appData.ringBellAfterMoves = TRUE;
\r
724 if (appData.debugMode) {
\r
725 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
726 setbuf(debugFP, NULL);
\r
731 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
732 // InitEngineUCI( installDir, &second );
\r
734 /* Create a main window for this application instance. */
\r
735 hwnd = CreateWindow(szAppName, szTitle,
\r
736 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
737 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
738 NULL, NULL, hInstance, NULL);
\r
741 /* If window could not be created, return "failure" */
\r
746 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
747 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
748 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
750 if (first.programLogo == NULL && appData.debugMode) {
\r
751 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
753 } else if(appData.autoLogo) {
\r
754 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
756 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
757 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
761 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
762 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
764 if (second.programLogo == NULL && appData.debugMode) {
\r
765 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
767 } else if(appData.autoLogo) {
\r
769 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
770 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
771 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
773 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
774 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
775 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
781 iconWhite = LoadIcon(hInstance, "icon_white");
\r
782 iconBlack = LoadIcon(hInstance, "icon_black");
\r
783 iconCurrent = iconWhite;
\r
784 InitDrawingColors();
\r
785 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
786 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
787 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
788 /* Compute window size for each board size, and use the largest
\r
789 size that fits on this screen as the default. */
\r
790 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
791 if (boardSize == (BoardSize)-1 &&
\r
792 winH <= screenHeight
\r
793 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
794 && winW <= screenWidth) {
\r
795 boardSize = (BoardSize)ibs;
\r
799 InitDrawingSizes(boardSize, 0);
\r
801 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
803 /* [AS] Load textures if specified */
\r
804 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
806 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
807 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
808 liteBackTextureMode = appData.liteBackTextureMode;
\r
810 if (liteBackTexture == NULL && appData.debugMode) {
\r
811 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
815 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
816 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
817 darkBackTextureMode = appData.darkBackTextureMode;
\r
819 if (darkBackTexture == NULL && appData.debugMode) {
\r
820 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
824 mysrandom( (unsigned) time(NULL) );
\r
826 /* [AS] Restore layout */
\r
827 if( wpMoveHistory.visible ) {
\r
828 MoveHistoryPopUp();
\r
831 if( wpEvalGraph.visible ) {
\r
835 if( wpEngineOutput.visible ) {
\r
836 EngineOutputPopUp();
\r
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 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
854 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
858 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
859 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
861 ShowWindow(hwndConsole, nCmdShow);
\r
863 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
864 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
873 HMENU hmenu = GetMenu(hwndMain);
\r
875 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
876 MF_BYCOMMAND|((appData.icsActive &&
\r
877 *appData.icsCommPort != NULLCHAR) ?
\r
878 MF_ENABLED : MF_GRAYED));
\r
879 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
880 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
881 MF_CHECKED : MF_UNCHECKED));
\r
884 //---------------------------------------------------------------------------------------------------------
\r
886 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
887 #define XBOARD FALSE
\r
889 #define OPTCHAR "/"
\r
890 #define SEPCHAR "="
\r
894 // front-end part of option handling
\r
897 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
899 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
900 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
903 lf->lfEscapement = 0;
\r
904 lf->lfOrientation = 0;
\r
905 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
906 lf->lfItalic = mfp->italic;
\r
907 lf->lfUnderline = mfp->underline;
\r
908 lf->lfStrikeOut = mfp->strikeout;
\r
909 lf->lfCharSet = mfp->charset;
\r
910 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
911 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
912 lf->lfQuality = DEFAULT_QUALITY;
\r
913 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
914 strcpy(lf->lfFaceName, mfp->faceName);
\r
918 CreateFontInMF(MyFont *mf)
\r
920 LFfromMFP(&mf->lf, &mf->mfp);
\r
921 if (mf->hf) DeleteObject(mf->hf);
\r
922 mf->hf = CreateFontIndirect(&mf->lf);
\r
925 // [HGM] This platform-dependent table provides the location for storing the color info
\r
927 colorVariable[] = {
\r
932 &highlightSquareColor,
\r
933 &premoveHighlightColor,
\r
935 &consoleBackgroundColor,
\r
936 &appData.fontForeColorWhite,
\r
937 &appData.fontBackColorWhite,
\r
938 &appData.fontForeColorBlack,
\r
939 &appData.fontBackColorBlack,
\r
940 &appData.evalHistColorWhite,
\r
941 &appData.evalHistColorBlack,
\r
942 &appData.highlightArrowColor,
\r
945 /* Command line font name parser. NULL name means do nothing.
\r
946 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
947 For backward compatibility, syntax without the colon is also
\r
948 accepted, but font names with digits in them won't work in that case.
\r
951 ParseFontName(char *name, MyFontParams *mfp)
\r
954 if (name == NULL) return;
\r
956 q = strchr(p, ':');
\r
958 if (q - p >= sizeof(mfp->faceName))
\r
959 ExitArgError("Font name too long:", name);
\r
960 memcpy(mfp->faceName, p, q - p);
\r
961 mfp->faceName[q - p] = NULLCHAR;
\r
965 while (*p && !isdigit(*p)) {
\r
967 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
968 ExitArgError("Font name too long:", name);
\r
970 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
973 if (!*p) ExitArgError("Font point size missing:", name);
\r
974 mfp->pointSize = (float) atof(p);
\r
975 mfp->bold = (strchr(p, 'b') != NULL);
\r
976 mfp->italic = (strchr(p, 'i') != NULL);
\r
977 mfp->underline = (strchr(p, 'u') != NULL);
\r
978 mfp->strikeout = (strchr(p, 's') != NULL);
\r
979 mfp->charset = DEFAULT_CHARSET;
\r
980 q = strchr(p, 'c');
\r
982 mfp->charset = (BYTE) atoi(q+1);
\r
986 ParseFont(char *name, int number)
\r
987 { // wrapper to shield back-end from 'font'
\r
988 ParseFontName(name, &font[boardSize][number]->mfp);
\r
993 { // in WB we have a 2D array of fonts; this initializes their description
\r
995 /* Point font array elements to structures and
\r
996 parse default font names */
\r
997 for (i=0; i<NUM_FONTS; i++) {
\r
998 for (j=0; j<NUM_SIZES; j++) {
\r
999 font[j][i] = &fontRec[j][i];
\r
1000 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1007 { // here we create the actual fonts from the selected descriptions
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 CreateFontInMF(font[j][i]);
\r
1015 /* Color name parser.
\r
1016 X version accepts X color names, but this one
\r
1017 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1019 ParseColorName(char *name)
\r
1021 int red, green, blue, count;
\r
1022 char buf[MSG_SIZ];
\r
1024 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1026 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1027 &red, &green, &blue);
\r
1030 sprintf(buf, "Can't parse color name %s", name);
\r
1031 DisplayError(buf, 0);
\r
1032 return RGB(0, 0, 0);
\r
1034 return PALETTERGB(red, green, blue);
\r
1038 ParseColor(int n, char *name)
\r
1039 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1040 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1044 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1046 char *e = argValue;
\r
1050 if (*e == 'b') eff |= CFE_BOLD;
\r
1051 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1052 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1053 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1054 else if (*e == '#' || isdigit(*e)) break;
\r
1058 *color = ParseColorName(e);
\r
1062 ParseTextAttribs(ColorClass cc, char *s)
\r
1063 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1064 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1065 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1069 ParseBoardSize(void *addr, char *name)
\r
1070 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1071 BoardSize bs = SizeTiny;
\r
1072 while (sizeInfo[bs].name != NULL) {
\r
1073 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1074 *(BoardSize *)addr = bs;
\r
1079 ExitArgError("Unrecognized board size value", name);
\r
1084 { // [HGM] import name from appData first
\r
1087 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1088 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1089 textAttribs[cc].sound.data = NULL;
\r
1090 MyLoadSound(&textAttribs[cc].sound);
\r
1092 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1093 textAttribs[cc].sound.name = strdup("");
\r
1094 textAttribs[cc].sound.data = NULL;
\r
1096 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1097 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1098 sounds[sc].data = NULL;
\r
1099 MyLoadSound(&sounds[sc]);
\r
1104 SetCommPortDefaults()
\r
1106 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1107 dcb.DCBlength = sizeof(DCB);
\r
1108 dcb.BaudRate = 9600;
\r
1109 dcb.fBinary = TRUE;
\r
1110 dcb.fParity = FALSE;
\r
1111 dcb.fOutxCtsFlow = FALSE;
\r
1112 dcb.fOutxDsrFlow = FALSE;
\r
1113 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1114 dcb.fDsrSensitivity = FALSE;
\r
1115 dcb.fTXContinueOnXoff = TRUE;
\r
1116 dcb.fOutX = FALSE;
\r
1118 dcb.fNull = FALSE;
\r
1119 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1120 dcb.fAbortOnError = FALSE;
\r
1122 dcb.Parity = SPACEPARITY;
\r
1123 dcb.StopBits = ONESTOPBIT;
\r
1126 // [HGM] args: these three cases taken out to stay in front-end
\r
1128 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1129 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1130 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1131 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1133 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1134 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1135 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1136 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1137 ad->argName, mfp->faceName, mfp->pointSize,
\r
1138 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1139 mfp->bold ? "b" : "",
\r
1140 mfp->italic ? "i" : "",
\r
1141 mfp->underline ? "u" : "",
\r
1142 mfp->strikeout ? "s" : "",
\r
1143 (int)mfp->charset);
\r
1149 { // [HGM] copy the names from the internal WB variables to appData
\r
1152 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1153 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1154 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1155 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1159 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1160 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1161 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1162 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1163 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1164 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1165 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1166 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1167 (ta->effects) ? " " : "",
\r
1168 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1172 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1173 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1174 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1175 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1176 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1180 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1181 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1182 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1186 ParseCommPortSettings(char *s)
\r
1187 { // wrapper to keep dcb from back-end
\r
1188 ParseCommSettings(s, &dcb);
\r
1193 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1194 GetActualPlacement(hwndMain, &wpMain);
\r
1195 GetActualPlacement(hwndConsole, &wpConsole);
\r
1196 GetActualPlacement(commentDialog, &wpComment);
\r
1197 GetActualPlacement(editTagsDialog, &wpTags);
\r
1198 GetActualPlacement(gameListDialog, &wpGameList);
\r
1199 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1200 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1201 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1205 PrintCommPortSettings(FILE *f, char *name)
\r
1206 { // wrapper to shield back-end from DCB
\r
1207 PrintCommSettings(f, name, &dcb);
\r
1211 MySearchPath(char *installDir, char *name, char *fullname)
\r
1214 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1218 MyGetFullPathName(char *name, char *fullname)
\r
1221 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1226 { // [HGM] args: allows testing if main window is realized from back-end
\r
1227 return hwndMain != NULL;
\r
1231 PopUpStartupDialog()
\r
1235 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1236 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1237 FreeProcInstance(lpProc);
\r
1240 /*---------------------------------------------------------------------------*\
\r
1242 * GDI board drawing routines
\r
1244 \*---------------------------------------------------------------------------*/
\r
1246 /* [AS] Draw square using background texture */
\r
1247 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1252 return; /* Should never happen! */
\r
1255 SetGraphicsMode( dst, GM_ADVANCED );
\r
1262 /* X reflection */
\r
1267 x.eDx = (FLOAT) dw + dx - 1;
\r
1270 SetWorldTransform( dst, &x );
\r
1273 /* Y reflection */
\r
1279 x.eDy = (FLOAT) dh + dy - 1;
\r
1281 SetWorldTransform( dst, &x );
\r
1289 x.eDx = (FLOAT) dx;
\r
1290 x.eDy = (FLOAT) dy;
\r
1293 SetWorldTransform( dst, &x );
\r
1297 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1305 SetWorldTransform( dst, &x );
\r
1307 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1310 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1312 PM_WP = (int) WhitePawn,
\r
1313 PM_WN = (int) WhiteKnight,
\r
1314 PM_WB = (int) WhiteBishop,
\r
1315 PM_WR = (int) WhiteRook,
\r
1316 PM_WQ = (int) WhiteQueen,
\r
1317 PM_WF = (int) WhiteFerz,
\r
1318 PM_WW = (int) WhiteWazir,
\r
1319 PM_WE = (int) WhiteAlfil,
\r
1320 PM_WM = (int) WhiteMan,
\r
1321 PM_WO = (int) WhiteCannon,
\r
1322 PM_WU = (int) WhiteUnicorn,
\r
1323 PM_WH = (int) WhiteNightrider,
\r
1324 PM_WA = (int) WhiteAngel,
\r
1325 PM_WC = (int) WhiteMarshall,
\r
1326 PM_WAB = (int) WhiteCardinal,
\r
1327 PM_WD = (int) WhiteDragon,
\r
1328 PM_WL = (int) WhiteLance,
\r
1329 PM_WS = (int) WhiteCobra,
\r
1330 PM_WV = (int) WhiteFalcon,
\r
1331 PM_WSG = (int) WhiteSilver,
\r
1332 PM_WG = (int) WhiteGrasshopper,
\r
1333 PM_WK = (int) WhiteKing,
\r
1334 PM_BP = (int) BlackPawn,
\r
1335 PM_BN = (int) BlackKnight,
\r
1336 PM_BB = (int) BlackBishop,
\r
1337 PM_BR = (int) BlackRook,
\r
1338 PM_BQ = (int) BlackQueen,
\r
1339 PM_BF = (int) BlackFerz,
\r
1340 PM_BW = (int) BlackWazir,
\r
1341 PM_BE = (int) BlackAlfil,
\r
1342 PM_BM = (int) BlackMan,
\r
1343 PM_BO = (int) BlackCannon,
\r
1344 PM_BU = (int) BlackUnicorn,
\r
1345 PM_BH = (int) BlackNightrider,
\r
1346 PM_BA = (int) BlackAngel,
\r
1347 PM_BC = (int) BlackMarshall,
\r
1348 PM_BG = (int) BlackGrasshopper,
\r
1349 PM_BAB = (int) BlackCardinal,
\r
1350 PM_BD = (int) BlackDragon,
\r
1351 PM_BL = (int) BlackLance,
\r
1352 PM_BS = (int) BlackCobra,
\r
1353 PM_BV = (int) BlackFalcon,
\r
1354 PM_BSG = (int) BlackSilver,
\r
1355 PM_BK = (int) BlackKing
\r
1358 static HFONT hPieceFont = NULL;
\r
1359 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1360 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1361 static int fontBitmapSquareSize = 0;
\r
1362 static char pieceToFontChar[(int) EmptySquare] =
\r
1363 { 'p', 'n', 'b', 'r', 'q',
\r
1364 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1365 'k', 'o', 'm', 'v', 't', 'w',
\r
1366 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1369 extern BOOL SetCharTable( char *table, const char * map );
\r
1370 /* [HGM] moved to backend.c */
\r
1372 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1375 BYTE r1 = GetRValue( color );
\r
1376 BYTE g1 = GetGValue( color );
\r
1377 BYTE b1 = GetBValue( color );
\r
1383 /* Create a uniform background first */
\r
1384 hbrush = CreateSolidBrush( color );
\r
1385 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1386 FillRect( hdc, &rc, hbrush );
\r
1387 DeleteObject( hbrush );
\r
1390 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1391 int steps = squareSize / 2;
\r
1394 for( i=0; i<steps; i++ ) {
\r
1395 BYTE r = r1 - (r1-r2) * i / steps;
\r
1396 BYTE g = g1 - (g1-g2) * i / steps;
\r
1397 BYTE b = b1 - (b1-b2) * i / steps;
\r
1399 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1400 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1401 FillRect( hdc, &rc, hbrush );
\r
1402 DeleteObject(hbrush);
\r
1405 else if( mode == 2 ) {
\r
1406 /* Diagonal gradient, good more or less for every piece */
\r
1407 POINT triangle[3];
\r
1408 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1409 HBRUSH hbrush_old;
\r
1410 int steps = squareSize;
\r
1413 triangle[0].x = squareSize - steps;
\r
1414 triangle[0].y = squareSize;
\r
1415 triangle[1].x = squareSize;
\r
1416 triangle[1].y = squareSize;
\r
1417 triangle[2].x = squareSize;
\r
1418 triangle[2].y = squareSize - steps;
\r
1420 for( i=0; i<steps; i++ ) {
\r
1421 BYTE r = r1 - (r1-r2) * i / steps;
\r
1422 BYTE g = g1 - (g1-g2) * i / steps;
\r
1423 BYTE b = b1 - (b1-b2) * i / steps;
\r
1425 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1426 hbrush_old = SelectObject( hdc, hbrush );
\r
1427 Polygon( hdc, triangle, 3 );
\r
1428 SelectObject( hdc, hbrush_old );
\r
1429 DeleteObject(hbrush);
\r
1434 SelectObject( hdc, hpen );
\r
1439 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1440 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1441 piece: follow the steps as explained below.
\r
1443 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1447 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1451 int backColor = whitePieceColor;
\r
1452 int foreColor = blackPieceColor;
\r
1454 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1455 backColor = appData.fontBackColorWhite;
\r
1456 foreColor = appData.fontForeColorWhite;
\r
1458 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1459 backColor = appData.fontBackColorBlack;
\r
1460 foreColor = appData.fontForeColorBlack;
\r
1464 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1466 hbm_old = SelectObject( hdc, hbm );
\r
1470 rc.right = squareSize;
\r
1471 rc.bottom = squareSize;
\r
1473 /* Step 1: background is now black */
\r
1474 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1476 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1478 pt.x = (squareSize - sz.cx) / 2;
\r
1479 pt.y = (squareSize - sz.cy) / 2;
\r
1481 SetBkMode( hdc, TRANSPARENT );
\r
1482 SetTextColor( hdc, chroma );
\r
1483 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1484 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1486 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1487 /* Step 3: the area outside the piece is filled with white */
\r
1488 // FloodFill( hdc, 0, 0, chroma );
\r
1489 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1490 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1491 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1492 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1493 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1495 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1496 but if the start point is not inside the piece we're lost!
\r
1497 There should be a better way to do this... if we could create a region or path
\r
1498 from the fill operation we would be fine for example.
\r
1500 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1501 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1503 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1504 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1505 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1507 SelectObject( dc2, bm2 );
\r
1508 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1509 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1510 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1511 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1512 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1515 DeleteObject( bm2 );
\r
1518 SetTextColor( hdc, 0 );
\r
1520 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1521 draw the piece again in black for safety.
\r
1523 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1525 SelectObject( hdc, hbm_old );
\r
1527 if( hPieceMask[index] != NULL ) {
\r
1528 DeleteObject( hPieceMask[index] );
\r
1531 hPieceMask[index] = hbm;
\r
1534 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1536 SelectObject( hdc, hbm );
\r
1539 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1540 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1541 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1543 SelectObject( dc1, hPieceMask[index] );
\r
1544 SelectObject( dc2, bm2 );
\r
1545 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1546 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1549 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1550 the piece background and deletes (makes transparent) the rest.
\r
1551 Thanks to that mask, we are free to paint the background with the greates
\r
1552 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1553 We use this, to make gradients and give the pieces a "roundish" look.
\r
1555 SetPieceBackground( hdc, backColor, 2 );
\r
1556 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1560 DeleteObject( bm2 );
\r
1563 SetTextColor( hdc, foreColor );
\r
1564 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1566 SelectObject( hdc, hbm_old );
\r
1568 if( hPieceFace[index] != NULL ) {
\r
1569 DeleteObject( hPieceFace[index] );
\r
1572 hPieceFace[index] = hbm;
\r
1575 static int TranslatePieceToFontPiece( int piece )
\r
1605 case BlackMarshall:
\r
1609 case BlackNightrider:
\r
1615 case BlackUnicorn:
\r
1619 case BlackGrasshopper:
\r
1631 case BlackCardinal:
\r
1638 case WhiteMarshall:
\r
1642 case WhiteNightrider:
\r
1648 case WhiteUnicorn:
\r
1652 case WhiteGrasshopper:
\r
1664 case WhiteCardinal:
\r
1673 void CreatePiecesFromFont()
\r
1676 HDC hdc_window = NULL;
\r
1682 if( fontBitmapSquareSize < 0 ) {
\r
1683 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1687 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1688 fontBitmapSquareSize = -1;
\r
1692 if( fontBitmapSquareSize != squareSize ) {
\r
1693 hdc_window = GetDC( hwndMain );
\r
1694 hdc = CreateCompatibleDC( hdc_window );
\r
1696 if( hPieceFont != NULL ) {
\r
1697 DeleteObject( hPieceFont );
\r
1700 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1701 hPieceMask[i] = NULL;
\r
1702 hPieceFace[i] = NULL;
\r
1708 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1709 fontHeight = appData.fontPieceSize;
\r
1712 fontHeight = (fontHeight * squareSize) / 100;
\r
1714 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1716 lf.lfEscapement = 0;
\r
1717 lf.lfOrientation = 0;
\r
1718 lf.lfWeight = FW_NORMAL;
\r
1720 lf.lfUnderline = 0;
\r
1721 lf.lfStrikeOut = 0;
\r
1722 lf.lfCharSet = DEFAULT_CHARSET;
\r
1723 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1724 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1725 lf.lfQuality = PROOF_QUALITY;
\r
1726 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1727 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1728 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1730 hPieceFont = CreateFontIndirect( &lf );
\r
1732 if( hPieceFont == NULL ) {
\r
1733 fontBitmapSquareSize = -2;
\r
1736 /* Setup font-to-piece character table */
\r
1737 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1738 /* No (or wrong) global settings, try to detect the font */
\r
1739 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1741 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1743 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1744 /* DiagramTT* family */
\r
1745 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1747 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1748 /* Fairy symbols */
\r
1749 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1751 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1752 /* Good Companion (Some characters get warped as literal :-( */
\r
1753 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1754 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1755 SetCharTable(pieceToFontChar, s);
\r
1758 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1759 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1763 /* Create bitmaps */
\r
1764 hfont_old = SelectObject( hdc, hPieceFont );
\r
1765 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1766 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1767 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1769 SelectObject( hdc, hfont_old );
\r
1771 fontBitmapSquareSize = squareSize;
\r
1775 if( hdc != NULL ) {
\r
1779 if( hdc_window != NULL ) {
\r
1780 ReleaseDC( hwndMain, hdc_window );
\r
1785 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1789 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1790 if (gameInfo.event &&
\r
1791 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1792 strcmp(name, "k80s") == 0) {
\r
1793 strcpy(name, "tim");
\r
1795 return LoadBitmap(hinst, name);
\r
1799 /* Insert a color into the program's logical palette
\r
1800 structure. This code assumes the given color is
\r
1801 the result of the RGB or PALETTERGB macro, and it
\r
1802 knows how those macros work (which is documented).
\r
1805 InsertInPalette(COLORREF color)
\r
1807 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1809 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1810 DisplayFatalError("Too many colors", 0, 1);
\r
1811 pLogPal->palNumEntries--;
\r
1815 pe->peFlags = (char) 0;
\r
1816 pe->peRed = (char) (0xFF & color);
\r
1817 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1818 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1824 InitDrawingColors()
\r
1826 if (pLogPal == NULL) {
\r
1827 /* Allocate enough memory for a logical palette with
\r
1828 * PALETTESIZE entries and set the size and version fields
\r
1829 * of the logical palette structure.
\r
1831 pLogPal = (NPLOGPALETTE)
\r
1832 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1833 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1834 pLogPal->palVersion = 0x300;
\r
1836 pLogPal->palNumEntries = 0;
\r
1838 InsertInPalette(lightSquareColor);
\r
1839 InsertInPalette(darkSquareColor);
\r
1840 InsertInPalette(whitePieceColor);
\r
1841 InsertInPalette(blackPieceColor);
\r
1842 InsertInPalette(highlightSquareColor);
\r
1843 InsertInPalette(premoveHighlightColor);
\r
1845 /* create a logical color palette according the information
\r
1846 * in the LOGPALETTE structure.
\r
1848 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1850 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1851 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1852 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1853 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1854 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1855 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1856 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1857 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1858 /* [AS] Force rendering of the font-based pieces */
\r
1859 if( fontBitmapSquareSize > 0 ) {
\r
1860 fontBitmapSquareSize = 0;
\r
1866 BoardWidth(int boardSize, int n)
\r
1867 { /* [HGM] argument n added to allow different width and height */
\r
1868 int lineGap = sizeInfo[boardSize].lineGap;
\r
1870 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1871 lineGap = appData.overrideLineGap;
\r
1874 return (n + 1) * lineGap +
\r
1875 n * sizeInfo[boardSize].squareSize;
\r
1878 /* Respond to board resize by dragging edge */
\r
1880 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1882 BoardSize newSize = NUM_SIZES - 1;
\r
1883 static int recurse = 0;
\r
1884 if (IsIconic(hwndMain)) return;
\r
1885 if (recurse > 0) return;
\r
1887 while (newSize > 0) {
\r
1888 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1889 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1890 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1893 boardSize = newSize;
\r
1894 InitDrawingSizes(boardSize, flags);
\r
1901 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1903 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1904 ChessSquare piece;
\r
1905 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1907 SIZE clockSize, messageSize;
\r
1909 char buf[MSG_SIZ];
\r
1911 HMENU hmenu = GetMenu(hwndMain);
\r
1912 RECT crect, wrect, oldRect;
\r
1914 LOGBRUSH logbrush;
\r
1916 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1917 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1919 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1920 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1922 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1923 oldRect.top = wpMain.y;
\r
1924 oldRect.right = wpMain.x + wpMain.width;
\r
1925 oldRect.bottom = wpMain.y + wpMain.height;
\r
1927 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1928 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1929 squareSize = sizeInfo[boardSize].squareSize;
\r
1930 lineGap = sizeInfo[boardSize].lineGap;
\r
1931 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1933 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1934 lineGap = appData.overrideLineGap;
\r
1937 if (tinyLayout != oldTinyLayout) {
\r
1938 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1940 style &= ~WS_SYSMENU;
\r
1941 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1942 "&Minimize\tCtrl+F4");
\r
1944 style |= WS_SYSMENU;
\r
1945 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1947 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1949 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1950 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1951 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1953 DrawMenuBar(hwndMain);
\r
1956 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1957 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1959 /* Get text area sizes */
\r
1960 hdc = GetDC(hwndMain);
\r
1961 if (appData.clockMode) {
\r
1962 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1964 sprintf(buf, "White");
\r
1966 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1967 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1968 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1969 str = "We only care about the height here";
\r
1970 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1971 SelectObject(hdc, oldFont);
\r
1972 ReleaseDC(hwndMain, hdc);
\r
1974 /* Compute where everything goes */
\r
1975 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1976 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1977 logoHeight = 2*clockSize.cy;
\r
1978 leftLogoRect.left = OUTER_MARGIN;
\r
1979 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1980 leftLogoRect.top = OUTER_MARGIN;
\r
1981 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1983 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1984 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1985 rightLogoRect.top = OUTER_MARGIN;
\r
1986 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1989 whiteRect.left = leftLogoRect.right;
\r
1990 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
1991 whiteRect.top = OUTER_MARGIN;
\r
1992 whiteRect.bottom = whiteRect.top + logoHeight;
\r
1994 blackRect.right = rightLogoRect.left;
\r
1995 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
1996 blackRect.top = whiteRect.top;
\r
1997 blackRect.bottom = whiteRect.bottom;
\r
1999 whiteRect.left = OUTER_MARGIN;
\r
2000 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2001 whiteRect.top = OUTER_MARGIN;
\r
2002 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2004 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2005 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2006 blackRect.top = whiteRect.top;
\r
2007 blackRect.bottom = whiteRect.bottom;
\r
2010 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2011 if (appData.showButtonBar) {
\r
2012 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2013 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2015 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2017 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2018 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2020 boardRect.left = OUTER_MARGIN;
\r
2021 boardRect.right = boardRect.left + boardWidth;
\r
2022 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2023 boardRect.bottom = boardRect.top + boardHeight;
\r
2025 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2026 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2027 oldBoardSize = boardSize;
\r
2028 oldTinyLayout = tinyLayout;
\r
2029 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2030 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2031 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2032 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2033 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2034 wpMain.height = winH; // without disturbing window attachments
\r
2035 GetWindowRect(hwndMain, &wrect);
\r
2036 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2037 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2039 // [HGM] placement: let attached windows follow size change.
\r
2040 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2041 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2042 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2043 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2044 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2046 /* compensate if menu bar wrapped */
\r
2047 GetClientRect(hwndMain, &crect);
\r
2048 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2049 wpMain.height += offby;
\r
2051 case WMSZ_TOPLEFT:
\r
2052 SetWindowPos(hwndMain, NULL,
\r
2053 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2054 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2057 case WMSZ_TOPRIGHT:
\r
2059 SetWindowPos(hwndMain, NULL,
\r
2060 wrect.left, wrect.bottom - wpMain.height,
\r
2061 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2064 case WMSZ_BOTTOMLEFT:
\r
2066 SetWindowPos(hwndMain, NULL,
\r
2067 wrect.right - wpMain.width, wrect.top,
\r
2068 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2071 case WMSZ_BOTTOMRIGHT:
\r
2075 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2076 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2081 for (i = 0; i < N_BUTTONS; i++) {
\r
2082 if (buttonDesc[i].hwnd != NULL) {
\r
2083 DestroyWindow(buttonDesc[i].hwnd);
\r
2084 buttonDesc[i].hwnd = NULL;
\r
2086 if (appData.showButtonBar) {
\r
2087 buttonDesc[i].hwnd =
\r
2088 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2089 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2090 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2091 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2092 (HMENU) buttonDesc[i].id,
\r
2093 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2095 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2096 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2097 MAKELPARAM(FALSE, 0));
\r
2099 if (buttonDesc[i].id == IDM_Pause)
\r
2100 hwndPause = buttonDesc[i].hwnd;
\r
2101 buttonDesc[i].wndproc = (WNDPROC)
\r
2102 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2105 if (gridPen != NULL) DeleteObject(gridPen);
\r
2106 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2107 if (premovePen != NULL) DeleteObject(premovePen);
\r
2108 if (lineGap != 0) {
\r
2109 logbrush.lbStyle = BS_SOLID;
\r
2110 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2112 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2113 lineGap, &logbrush, 0, NULL);
\r
2114 logbrush.lbColor = highlightSquareColor;
\r
2116 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2117 lineGap, &logbrush, 0, NULL);
\r
2119 logbrush.lbColor = premoveHighlightColor;
\r
2121 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2122 lineGap, &logbrush, 0, NULL);
\r
2124 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2125 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2126 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2127 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2128 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2129 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2130 BOARD_WIDTH * (squareSize + lineGap);
\r
2131 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2133 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2134 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2135 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2136 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2137 lineGap / 2 + (i * (squareSize + lineGap));
\r
2138 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2139 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2140 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2144 /* [HGM] Licensing requirement */
\r
2146 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2149 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2151 GothicPopUp( "", VariantNormal);
\r
2154 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2156 /* Load piece bitmaps for this board size */
\r
2157 for (i=0; i<=2; i++) {
\r
2158 for (piece = WhitePawn;
\r
2159 (int) piece < (int) BlackPawn;
\r
2160 piece = (ChessSquare) ((int) piece + 1)) {
\r
2161 if (pieceBitmap[i][piece] != NULL)
\r
2162 DeleteObject(pieceBitmap[i][piece]);
\r
2166 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2167 // Orthodox Chess pieces
\r
2168 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2169 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2170 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2171 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2172 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2173 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2174 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2175 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2176 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2177 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2178 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2179 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2180 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2181 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2182 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2183 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2184 // in Shogi, Hijack the unused Queen for Lance
\r
2185 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2186 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2187 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2189 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2190 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2191 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2194 if(squareSize <= 72 && squareSize >= 33) {
\r
2195 /* A & C are available in most sizes now */
\r
2196 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2197 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2198 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2199 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2200 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2201 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2202 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2203 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2204 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2205 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2206 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2207 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2208 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2209 } else { // Smirf-like
\r
2210 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2211 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2212 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2214 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2215 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2216 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2217 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2218 } else { // WinBoard standard
\r
2219 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2220 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2221 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2226 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2227 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2228 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2229 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2230 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2231 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2232 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2233 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2236 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2237 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2238 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2239 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2240 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2241 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2242 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2245 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2246 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2247 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2248 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2249 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2250 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2251 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2252 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2253 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2254 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2255 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2256 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2258 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2259 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2272 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2273 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2274 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2275 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2276 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2277 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2278 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2279 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2280 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2281 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2282 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2283 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2286 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2287 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2288 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2289 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2290 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2291 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2292 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2293 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2294 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2295 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2296 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2297 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2298 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2299 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2300 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2304 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2305 /* special Shogi support in this size */
\r
2306 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2307 for (piece = WhitePawn;
\r
2308 (int) piece < (int) BlackPawn;
\r
2309 piece = (ChessSquare) ((int) piece + 1)) {
\r
2310 if (pieceBitmap[i][piece] != NULL)
\r
2311 DeleteObject(pieceBitmap[i][piece]);
\r
2314 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2315 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2316 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2317 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2318 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2319 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2320 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2321 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2322 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2323 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2324 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2325 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2326 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2327 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2328 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2329 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2330 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2331 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2332 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2333 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2334 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2335 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2336 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2337 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2338 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2339 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2340 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2341 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2342 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2343 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2344 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2345 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2346 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2347 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2348 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2349 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2350 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2351 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2352 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2353 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2354 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2355 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2361 PieceBitmap(ChessSquare p, int kind)
\r
2363 if ((int) p >= (int) BlackPawn)
\r
2364 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2366 return pieceBitmap[kind][(int) p];
\r
2369 /***************************************************************/
\r
2371 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2372 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2374 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2375 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2379 SquareToPos(int row, int column, int * x, int * y)
\r
2382 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2383 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2385 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2386 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2391 DrawCoordsOnDC(HDC hdc)
\r
2393 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
2394 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
2395 char str[2] = { NULLCHAR, NULLCHAR };
\r
2396 int oldMode, oldAlign, x, y, start, i;
\r
2400 if (!appData.showCoords)
\r
2403 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2405 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2406 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2407 oldAlign = GetTextAlign(hdc);
\r
2408 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2410 y = boardRect.top + lineGap;
\r
2411 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2413 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2414 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2415 str[0] = files[start + i];
\r
2416 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2417 y += squareSize + lineGap;
\r
2420 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2422 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2423 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2424 str[0] = ranks[start + i];
\r
2425 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2426 x += squareSize + lineGap;
\r
2429 SelectObject(hdc, oldBrush);
\r
2430 SetBkMode(hdc, oldMode);
\r
2431 SetTextAlign(hdc, oldAlign);
\r
2432 SelectObject(hdc, oldFont);
\r
2436 DrawGridOnDC(HDC hdc)
\r
2440 if (lineGap != 0) {
\r
2441 oldPen = SelectObject(hdc, gridPen);
\r
2442 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2443 SelectObject(hdc, oldPen);
\r
2447 #define HIGHLIGHT_PEN 0
\r
2448 #define PREMOVE_PEN 1
\r
2451 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2454 HPEN oldPen, hPen;
\r
2455 if (lineGap == 0) return;
\r
2457 x1 = boardRect.left +
\r
2458 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2459 y1 = boardRect.top +
\r
2460 lineGap/2 + y * (squareSize + lineGap);
\r
2462 x1 = boardRect.left +
\r
2463 lineGap/2 + x * (squareSize + lineGap);
\r
2464 y1 = boardRect.top +
\r
2465 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2467 hPen = pen ? premovePen : highlightPen;
\r
2468 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2469 MoveToEx(hdc, x1, y1, NULL);
\r
2470 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2471 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2472 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2473 LineTo(hdc, x1, y1);
\r
2474 SelectObject(hdc, oldPen);
\r
2478 DrawHighlightsOnDC(HDC hdc)
\r
2481 for (i=0; i<2; i++) {
\r
2482 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2483 DrawHighlightOnDC(hdc, TRUE,
\r
2484 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2487 for (i=0; i<2; i++) {
\r
2488 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2489 premoveHighlightInfo.sq[i].y >= 0) {
\r
2490 DrawHighlightOnDC(hdc, TRUE,
\r
2491 premoveHighlightInfo.sq[i].x,
\r
2492 premoveHighlightInfo.sq[i].y,
\r
2498 /* Note: sqcolor is used only in monoMode */
\r
2499 /* Note that this code is largely duplicated in woptions.c,
\r
2500 function DrawSampleSquare, so that needs to be updated too */
\r
2502 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2504 HBITMAP oldBitmap;
\r
2508 if (appData.blindfold) return;
\r
2510 /* [AS] Use font-based pieces if needed */
\r
2511 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2512 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2513 CreatePiecesFromFont();
\r
2515 if( fontBitmapSquareSize == squareSize ) {
\r
2516 int index = TranslatePieceToFontPiece(piece);
\r
2518 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2522 squareSize, squareSize,
\r
2527 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2531 squareSize, squareSize,
\r
2540 if (appData.monoMode) {
\r
2541 SelectObject(tmphdc, PieceBitmap(piece,
\r
2542 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2543 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2544 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2546 tmpSize = squareSize;
\r
2548 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2549 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2550 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2551 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2552 x += (squareSize - minorSize)>>1;
\r
2553 y += squareSize - minorSize - 2;
\r
2554 tmpSize = minorSize;
\r
2556 if (color || appData.allWhite ) {
\r
2557 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2559 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2560 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2561 if(appData.upsideDown && color==flipView)
\r
2562 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2564 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2565 /* Use black for outline of white pieces */
\r
2566 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2567 if(appData.upsideDown && color==flipView)
\r
2568 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2570 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2572 /* Use square color for details of black pieces */
\r
2573 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2574 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2575 if(appData.upsideDown && !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
2580 SelectObject(hdc, oldBrush);
\r
2581 SelectObject(tmphdc, oldBitmap);
\r
2585 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2586 int GetBackTextureMode( int algo )
\r
2588 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2592 case BACK_TEXTURE_MODE_PLAIN:
\r
2593 result = 1; /* Always use identity map */
\r
2595 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2596 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2604 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2605 to handle redraws cleanly (as random numbers would always be different).
\r
2607 VOID RebuildTextureSquareInfo()
\r
2617 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2619 if( liteBackTexture != NULL ) {
\r
2620 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2621 lite_w = bi.bmWidth;
\r
2622 lite_h = bi.bmHeight;
\r
2626 if( darkBackTexture != NULL ) {
\r
2627 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2628 dark_w = bi.bmWidth;
\r
2629 dark_h = bi.bmHeight;
\r
2633 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2634 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2635 if( (col + row) & 1 ) {
\r
2637 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2638 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2639 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2640 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2645 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2646 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2647 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2648 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2655 /* [AS] Arrow highlighting support */
\r
2657 static int A_WIDTH = 5; /* Width of arrow body */
\r
2659 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2660 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2662 static double Sqr( double x )
\r
2667 static int Round( double x )
\r
2669 return (int) (x + 0.5);
\r
2672 /* Draw an arrow between two points using current settings */
\r
2673 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2676 double dx, dy, j, k, x, y;
\r
2678 if( d_x == s_x ) {
\r
2679 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2681 arrow[0].x = s_x + A_WIDTH;
\r
2684 arrow[1].x = s_x + A_WIDTH;
\r
2685 arrow[1].y = d_y - h;
\r
2687 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2688 arrow[2].y = d_y - h;
\r
2693 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2694 arrow[4].y = d_y - h;
\r
2696 arrow[5].x = s_x - A_WIDTH;
\r
2697 arrow[5].y = d_y - h;
\r
2699 arrow[6].x = s_x - A_WIDTH;
\r
2702 else if( d_y == s_y ) {
\r
2703 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2706 arrow[0].y = s_y + A_WIDTH;
\r
2708 arrow[1].x = d_x - w;
\r
2709 arrow[1].y = s_y + A_WIDTH;
\r
2711 arrow[2].x = d_x - w;
\r
2712 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2717 arrow[4].x = d_x - w;
\r
2718 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2720 arrow[5].x = d_x - w;
\r
2721 arrow[5].y = s_y - A_WIDTH;
\r
2724 arrow[6].y = s_y - A_WIDTH;
\r
2727 /* [AS] Needed a lot of paper for this! :-) */
\r
2728 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2729 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2731 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2733 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2738 arrow[0].x = Round(x - j);
\r
2739 arrow[0].y = Round(y + j*dx);
\r
2741 arrow[1].x = Round(x + j);
\r
2742 arrow[1].y = Round(y - j*dx);
\r
2745 x = (double) d_x - k;
\r
2746 y = (double) d_y - k*dy;
\r
2749 x = (double) d_x + k;
\r
2750 y = (double) d_y + k*dy;
\r
2753 arrow[2].x = Round(x + j);
\r
2754 arrow[2].y = Round(y - j*dx);
\r
2756 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2757 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2762 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2763 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2765 arrow[6].x = Round(x - j);
\r
2766 arrow[6].y = Round(y + j*dx);
\r
2769 Polygon( hdc, arrow, 7 );
\r
2772 /* [AS] Draw an arrow between two squares */
\r
2773 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2775 int s_x, s_y, d_x, d_y;
\r
2782 if( s_col == d_col && s_row == d_row ) {
\r
2786 /* Get source and destination points */
\r
2787 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2788 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2791 d_y += squareSize / 4;
\r
2793 else if( d_y < s_y ) {
\r
2794 d_y += 3 * squareSize / 4;
\r
2797 d_y += squareSize / 2;
\r
2801 d_x += squareSize / 4;
\r
2803 else if( d_x < s_x ) {
\r
2804 d_x += 3 * squareSize / 4;
\r
2807 d_x += squareSize / 2;
\r
2810 s_x += squareSize / 2;
\r
2811 s_y += squareSize / 2;
\r
2813 /* Adjust width */
\r
2814 A_WIDTH = squareSize / 14;
\r
2817 stLB.lbStyle = BS_SOLID;
\r
2818 stLB.lbColor = appData.highlightArrowColor;
\r
2821 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2822 holdpen = SelectObject( hdc, hpen );
\r
2823 hbrush = CreateBrushIndirect( &stLB );
\r
2824 holdbrush = SelectObject( hdc, hbrush );
\r
2826 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2828 SelectObject( hdc, holdpen );
\r
2829 SelectObject( hdc, holdbrush );
\r
2830 DeleteObject( hpen );
\r
2831 DeleteObject( hbrush );
\r
2834 BOOL HasHighlightInfo()
\r
2836 BOOL result = FALSE;
\r
2838 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2839 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2847 BOOL IsDrawArrowEnabled()
\r
2849 BOOL result = FALSE;
\r
2851 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2858 VOID DrawArrowHighlight( HDC hdc )
\r
2860 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2861 DrawArrowBetweenSquares( hdc,
\r
2862 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2863 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2867 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2869 HRGN result = NULL;
\r
2871 if( HasHighlightInfo() ) {
\r
2872 int x1, y1, x2, y2;
\r
2873 int sx, sy, dx, dy;
\r
2875 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2876 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2878 sx = MIN( x1, x2 );
\r
2879 sy = MIN( y1, y2 );
\r
2880 dx = MAX( x1, x2 ) + squareSize;
\r
2881 dy = MAX( y1, y2 ) + squareSize;
\r
2883 result = CreateRectRgn( sx, sy, dx, dy );
\r
2890 Warning: this function modifies the behavior of several other functions.
\r
2892 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2893 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2894 repaint is scattered all over the place, which is not good for features such as
\r
2895 "arrow highlighting" that require a full repaint of the board.
\r
2897 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2898 user interaction, when speed is not so important) but especially to avoid errors
\r
2899 in the displayed graphics.
\r
2901 In such patched places, I always try refer to this function so there is a single
\r
2902 place to maintain knowledge.
\r
2904 To restore the original behavior, just return FALSE unconditionally.
\r
2906 BOOL IsFullRepaintPreferrable()
\r
2908 BOOL result = FALSE;
\r
2910 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2911 /* Arrow may appear on the board */
\r
2919 This function is called by DrawPosition to know whether a full repaint must
\r
2922 Only DrawPosition may directly call this function, which makes use of
\r
2923 some state information. Other function should call DrawPosition specifying
\r
2924 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2926 BOOL DrawPositionNeedsFullRepaint()
\r
2928 BOOL result = FALSE;
\r
2931 Probably a slightly better policy would be to trigger a full repaint
\r
2932 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2933 but animation is fast enough that it's difficult to notice.
\r
2935 if( animInfo.piece == EmptySquare ) {
\r
2936 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2945 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2947 int row, column, x, y, square_color, piece_color;
\r
2948 ChessSquare piece;
\r
2950 HDC texture_hdc = NULL;
\r
2952 /* [AS] Initialize background textures if needed */
\r
2953 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2954 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2955 if( backTextureSquareSize != squareSize
\r
2956 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2957 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2958 backTextureSquareSize = squareSize;
\r
2959 RebuildTextureSquareInfo();
\r
2962 texture_hdc = CreateCompatibleDC( hdc );
\r
2965 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2966 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2968 SquareToPos(row, column, &x, &y);
\r
2970 piece = board[row][column];
\r
2972 square_color = ((column + row) % 2) == 1;
\r
2973 if( gameInfo.variant == VariantXiangqi ) {
\r
2974 square_color = !InPalace(row, column);
\r
2975 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2976 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2978 piece_color = (int) piece < (int) BlackPawn;
\r
2981 /* [HGM] holdings file: light square or black */
\r
2982 if(column == BOARD_LEFT-2) {
\r
2983 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2986 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
2990 if(column == BOARD_RGHT + 1 ) {
\r
2991 if( row < gameInfo.holdingsSize )
\r
2994 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
2998 if(column == BOARD_LEFT-1 ) /* left align */
\r
2999 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3000 else if( column == BOARD_RGHT) /* right align */
\r
3001 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3003 if (appData.monoMode) {
\r
3004 if (piece == EmptySquare) {
\r
3005 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3006 square_color ? WHITENESS : BLACKNESS);
\r
3008 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3011 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3012 /* [AS] Draw the square using a texture bitmap */
\r
3013 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3014 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3015 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3018 squareSize, squareSize,
\r
3021 backTextureSquareInfo[r][c].mode,
\r
3022 backTextureSquareInfo[r][c].x,
\r
3023 backTextureSquareInfo[r][c].y );
\r
3025 SelectObject( texture_hdc, hbm );
\r
3027 if (piece != EmptySquare) {
\r
3028 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3032 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3034 oldBrush = SelectObject(hdc, brush );
\r
3035 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3036 SelectObject(hdc, oldBrush);
\r
3037 if (piece != EmptySquare)
\r
3038 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3043 if( texture_hdc != NULL ) {
\r
3044 DeleteDC( texture_hdc );
\r
3048 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3049 void fputDW(FILE *f, int x)
\r
3051 fputc(x & 255, f);
\r
3052 fputc(x>>8 & 255, f);
\r
3053 fputc(x>>16 & 255, f);
\r
3054 fputc(x>>24 & 255, f);
\r
3057 #define MAX_CLIPS 200 /* more than enough */
\r
3060 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3062 // HBITMAP bufferBitmap;
\r
3067 int w = 100, h = 50;
\r
3069 if(logo == NULL) return;
\r
3070 // GetClientRect(hwndMain, &Rect);
\r
3071 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3072 // Rect.bottom-Rect.top+1);
\r
3073 tmphdc = CreateCompatibleDC(hdc);
\r
3074 hbm = SelectObject(tmphdc, logo);
\r
3075 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3079 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3080 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3081 SelectObject(tmphdc, hbm);
\r
3086 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3088 static Board lastReq, lastDrawn;
\r
3089 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3090 static int lastDrawnFlipView = 0;
\r
3091 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3092 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3095 HBITMAP bufferBitmap;
\r
3096 HBITMAP oldBitmap;
\r
3098 HRGN clips[MAX_CLIPS];
\r
3099 ChessSquare dragged_piece = EmptySquare;
\r
3101 /* I'm undecided on this - this function figures out whether a full
\r
3102 * repaint is necessary on its own, so there's no real reason to have the
\r
3103 * caller tell it that. I think this can safely be set to FALSE - but
\r
3104 * if we trust the callers not to request full repaints unnessesarily, then
\r
3105 * we could skip some clipping work. In other words, only request a full
\r
3106 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3107 * gamestart and similar) --Hawk
\r
3109 Boolean fullrepaint = repaint;
\r
3111 if( DrawPositionNeedsFullRepaint() ) {
\r
3112 fullrepaint = TRUE;
\r
3115 if (board == NULL) {
\r
3116 if (!lastReqValid) {
\r
3121 CopyBoard(lastReq, board);
\r
3125 if (doingSizing) {
\r
3129 if (IsIconic(hwndMain)) {
\r
3133 if (hdc == NULL) {
\r
3134 hdc = GetDC(hwndMain);
\r
3135 if (!appData.monoMode) {
\r
3136 SelectPalette(hdc, hPal, FALSE);
\r
3137 RealizePalette(hdc);
\r
3141 releaseDC = FALSE;
\r
3144 /* Create some work-DCs */
\r
3145 hdcmem = CreateCompatibleDC(hdc);
\r
3146 tmphdc = CreateCompatibleDC(hdc);
\r
3148 /* If dragging is in progress, we temporarely remove the piece */
\r
3149 /* [HGM] or temporarily decrease count if stacked */
\r
3150 /* !! Moved to before board compare !! */
\r
3151 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3152 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3153 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3154 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3155 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3157 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3158 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3159 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3161 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3164 /* Figure out which squares need updating by comparing the
\r
3165 * newest board with the last drawn board and checking if
\r
3166 * flipping has changed.
\r
3168 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3169 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3170 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3171 if (lastDrawn[row][column] != board[row][column]) {
\r
3172 SquareToPos(row, column, &x, &y);
\r
3173 clips[num_clips++] =
\r
3174 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3178 for (i=0; i<2; i++) {
\r
3179 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3180 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3181 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3182 lastDrawnHighlight.sq[i].y >= 0) {
\r
3183 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3184 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3185 clips[num_clips++] =
\r
3186 CreateRectRgn(x - lineGap, y - lineGap,
\r
3187 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3189 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3190 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3191 clips[num_clips++] =
\r
3192 CreateRectRgn(x - lineGap, y - lineGap,
\r
3193 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3197 for (i=0; i<2; i++) {
\r
3198 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3199 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3200 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3201 lastDrawnPremove.sq[i].y >= 0) {
\r
3202 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3203 lastDrawnPremove.sq[i].x, &x, &y);
\r
3204 clips[num_clips++] =
\r
3205 CreateRectRgn(x - lineGap, y - lineGap,
\r
3206 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3208 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3209 premoveHighlightInfo.sq[i].y >= 0) {
\r
3210 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3211 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3212 clips[num_clips++] =
\r
3213 CreateRectRgn(x - lineGap, y - lineGap,
\r
3214 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3219 fullrepaint = TRUE;
\r
3222 /* Create a buffer bitmap - this is the actual bitmap
\r
3223 * being written to. When all the work is done, we can
\r
3224 * copy it to the real DC (the screen). This avoids
\r
3225 * the problems with flickering.
\r
3227 GetClientRect(hwndMain, &Rect);
\r
3228 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3229 Rect.bottom-Rect.top+1);
\r
3230 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3231 if (!appData.monoMode) {
\r
3232 SelectPalette(hdcmem, hPal, FALSE);
\r
3235 /* Create clips for dragging */
\r
3236 if (!fullrepaint) {
\r
3237 if (dragInfo.from.x >= 0) {
\r
3238 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3239 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3241 if (dragInfo.start.x >= 0) {
\r
3242 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3243 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3245 if (dragInfo.pos.x >= 0) {
\r
3246 x = dragInfo.pos.x - squareSize / 2;
\r
3247 y = dragInfo.pos.y - squareSize / 2;
\r
3248 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3250 if (dragInfo.lastpos.x >= 0) {
\r
3251 x = dragInfo.lastpos.x - squareSize / 2;
\r
3252 y = dragInfo.lastpos.y - squareSize / 2;
\r
3253 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3257 /* Are we animating a move?
\r
3259 * - remove the piece from the board (temporarely)
\r
3260 * - calculate the clipping region
\r
3262 if (!fullrepaint) {
\r
3263 if (animInfo.piece != EmptySquare) {
\r
3264 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3265 x = boardRect.left + animInfo.lastpos.x;
\r
3266 y = boardRect.top + animInfo.lastpos.y;
\r
3267 x2 = boardRect.left + animInfo.pos.x;
\r
3268 y2 = boardRect.top + animInfo.pos.y;
\r
3269 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3270 /* Slight kludge. The real problem is that after AnimateMove is
\r
3271 done, the position on the screen does not match lastDrawn.
\r
3272 This currently causes trouble only on e.p. captures in
\r
3273 atomic, where the piece moves to an empty square and then
\r
3274 explodes. The old and new positions both had an empty square
\r
3275 at the destination, but animation has drawn a piece there and
\r
3276 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3277 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3281 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3282 if (num_clips == 0)
\r
3283 fullrepaint = TRUE;
\r
3285 /* Set clipping on the memory DC */
\r
3286 if (!fullrepaint) {
\r
3287 SelectClipRgn(hdcmem, clips[0]);
\r
3288 for (x = 1; x < num_clips; x++) {
\r
3289 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3290 abort(); // this should never ever happen!
\r
3294 /* Do all the drawing to the memory DC */
\r
3295 if(explodeInfo.radius) { // [HGM] atomic
\r
3297 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3298 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3299 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3300 x += squareSize/2;
\r
3301 y += squareSize/2;
\r
3302 if(!fullrepaint) {
\r
3303 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3304 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3306 DrawGridOnDC(hdcmem);
\r
3307 DrawHighlightsOnDC(hdcmem);
\r
3308 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3309 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3310 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3311 SelectObject(hdcmem, oldBrush);
\r
3313 DrawGridOnDC(hdcmem);
\r
3314 DrawHighlightsOnDC(hdcmem);
\r
3315 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3317 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3318 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3319 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3320 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3321 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3322 SquareToPos(row, column, &x, &y);
\r
3323 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3324 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3325 SelectObject(hdcmem, oldBrush);
\r
3330 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3331 if(appData.autoLogo) {
\r
3333 switch(gameMode) { // pick logos based on game mode
\r
3334 case IcsObserving:
\r
3335 whiteLogo = second.programLogo; // ICS logo
\r
3336 blackLogo = second.programLogo;
\r
3339 case IcsPlayingWhite:
\r
3340 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3341 blackLogo = second.programLogo; // ICS logo
\r
3343 case IcsPlayingBlack:
\r
3344 whiteLogo = second.programLogo; // ICS logo
\r
3345 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3347 case TwoMachinesPlay:
\r
3348 if(first.twoMachinesColor[0] == 'b') {
\r
3349 whiteLogo = second.programLogo;
\r
3350 blackLogo = first.programLogo;
\r
3353 case MachinePlaysWhite:
\r
3354 blackLogo = userLogo;
\r
3356 case MachinePlaysBlack:
\r
3357 whiteLogo = userLogo;
\r
3358 blackLogo = first.programLogo;
\r
3361 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3362 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3365 if( appData.highlightMoveWithArrow ) {
\r
3366 DrawArrowHighlight(hdcmem);
\r
3369 DrawCoordsOnDC(hdcmem);
\r
3371 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3372 /* to make sure lastDrawn contains what is actually drawn */
\r
3374 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3375 if (dragged_piece != EmptySquare) {
\r
3376 /* [HGM] or restack */
\r
3377 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3378 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3380 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3381 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3382 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3383 x = dragInfo.pos.x - squareSize / 2;
\r
3384 y = dragInfo.pos.y - squareSize / 2;
\r
3385 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3386 ((int) dragged_piece < (int) BlackPawn),
\r
3387 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3390 /* Put the animated piece back into place and draw it */
\r
3391 if (animInfo.piece != EmptySquare) {
\r
3392 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3393 x = boardRect.left + animInfo.pos.x;
\r
3394 y = boardRect.top + animInfo.pos.y;
\r
3395 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3396 ((int) animInfo.piece < (int) BlackPawn),
\r
3397 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3400 /* Release the bufferBitmap by selecting in the old bitmap
\r
3401 * and delete the memory DC
\r
3403 SelectObject(hdcmem, oldBitmap);
\r
3406 /* Set clipping on the target DC */
\r
3407 if (!fullrepaint) {
\r
3408 SelectClipRgn(hdc, clips[0]);
\r
3409 for (x = 1; x < num_clips; x++) {
\r
3410 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3411 abort(); // this should never ever happen!
\r
3415 /* Copy the new bitmap onto the screen in one go.
\r
3416 * This way we avoid any flickering
\r
3418 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3419 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3420 boardRect.right - boardRect.left,
\r
3421 boardRect.bottom - boardRect.top,
\r
3422 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3423 if(saveDiagFlag) {
\r
3424 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3425 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3427 GetObject(bufferBitmap, sizeof(b), &b);
\r
3428 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3429 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3430 bih.biWidth = b.bmWidth;
\r
3431 bih.biHeight = b.bmHeight;
\r
3433 bih.biBitCount = b.bmBitsPixel;
\r
3434 bih.biCompression = 0;
\r
3435 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3436 bih.biXPelsPerMeter = 0;
\r
3437 bih.biYPelsPerMeter = 0;
\r
3438 bih.biClrUsed = 0;
\r
3439 bih.biClrImportant = 0;
\r
3440 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3441 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3442 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3443 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3445 wb = b.bmWidthBytes;
\r
3447 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3448 int k = ((int*) pData)[i];
\r
3449 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3450 if(j >= 16) break;
\r
3452 if(j >= nrColors) nrColors = j+1;
\r
3454 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3456 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3457 for(w=0; w<(wb>>2); w+=2) {
\r
3458 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3459 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3460 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3461 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3462 pData[p++] = m | j<<4;
\r
3464 while(p&3) pData[p++] = 0;
\r
3467 wb = ((wb+31)>>5)<<2;
\r
3469 // write BITMAPFILEHEADER
\r
3470 fprintf(diagFile, "BM");
\r
3471 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3472 fputDW(diagFile, 0);
\r
3473 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3474 // write BITMAPINFOHEADER
\r
3475 fputDW(diagFile, 40);
\r
3476 fputDW(diagFile, b.bmWidth);
\r
3477 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3478 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3479 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3480 fputDW(diagFile, 0);
\r
3481 fputDW(diagFile, 0);
\r
3482 fputDW(diagFile, 0);
\r
3483 fputDW(diagFile, 0);
\r
3484 fputDW(diagFile, 0);
\r
3485 fputDW(diagFile, 0);
\r
3486 // write color table
\r
3488 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3489 // write bitmap data
\r
3490 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3491 fputc(pData[i], diagFile);
\r
3495 SelectObject(tmphdc, oldBitmap);
\r
3497 /* Massive cleanup */
\r
3498 for (x = 0; x < num_clips; x++)
\r
3499 DeleteObject(clips[x]);
\r
3502 DeleteObject(bufferBitmap);
\r
3505 ReleaseDC(hwndMain, hdc);
\r
3507 if (lastDrawnFlipView != flipView) {
\r
3509 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3511 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3514 /* CopyBoard(lastDrawn, board);*/
\r
3515 lastDrawnHighlight = highlightInfo;
\r
3516 lastDrawnPremove = premoveHighlightInfo;
\r
3517 lastDrawnFlipView = flipView;
\r
3518 lastDrawnValid = 1;
\r
3521 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3526 saveDiagFlag = 1; diagFile = f;
\r
3527 HDCDrawPosition(NULL, TRUE, NULL);
\r
3531 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3538 /*---------------------------------------------------------------------------*\
\r
3539 | CLIENT PAINT PROCEDURE
\r
3540 | This is the main event-handler for the WM_PAINT message.
\r
3542 \*---------------------------------------------------------------------------*/
\r
3544 PaintProc(HWND hwnd)
\r
3550 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3551 if (IsIconic(hwnd)) {
\r
3552 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3554 if (!appData.monoMode) {
\r
3555 SelectPalette(hdc, hPal, FALSE);
\r
3556 RealizePalette(hdc);
\r
3558 HDCDrawPosition(hdc, 1, NULL);
\r
3560 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3561 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3562 ETO_CLIPPED|ETO_OPAQUE,
\r
3563 &messageRect, messageText, strlen(messageText), NULL);
\r
3564 SelectObject(hdc, oldFont);
\r
3565 DisplayBothClocks();
\r
3567 EndPaint(hwnd,&ps);
\r
3575 * If the user selects on a border boundary, return -1; if off the board,
\r
3576 * return -2. Otherwise map the event coordinate to the square.
\r
3577 * The offset boardRect.left or boardRect.top must already have been
\r
3578 * subtracted from x.
\r
3580 int EventToSquare(x, limit)
\r
3588 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3590 x /= (squareSize + lineGap);
\r
3602 DropEnable dropEnables[] = {
\r
3603 { 'P', DP_Pawn, "Pawn" },
\r
3604 { 'N', DP_Knight, "Knight" },
\r
3605 { 'B', DP_Bishop, "Bishop" },
\r
3606 { 'R', DP_Rook, "Rook" },
\r
3607 { 'Q', DP_Queen, "Queen" },
\r
3611 SetupDropMenu(HMENU hmenu)
\r
3613 int i, count, enable;
\r
3615 extern char white_holding[], black_holding[];
\r
3616 char item[MSG_SIZ];
\r
3618 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3619 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3620 dropEnables[i].piece);
\r
3622 while (p && *p++ == dropEnables[i].piece) count++;
\r
3623 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3624 enable = count > 0 || !appData.testLegality
\r
3625 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3626 && !appData.icsActive);
\r
3627 ModifyMenu(hmenu, dropEnables[i].command,
\r
3628 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3629 dropEnables[i].command, item);
\r
3633 void DragPieceBegin(int x, int y)
\r
3635 dragInfo.lastpos.x = boardRect.left + x;
\r
3636 dragInfo.lastpos.y = boardRect.top + y;
\r
3637 dragInfo.from.x = fromX;
\r
3638 dragInfo.from.y = fromY;
\r
3639 dragInfo.start = dragInfo.from;
\r
3640 SetCapture(hwndMain);
\r
3643 void DragPieceEnd(int x, int y)
\r
3646 dragInfo.start.x = dragInfo.start.y = -1;
\r
3647 dragInfo.from = dragInfo.start;
\r
3648 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3651 /* Event handler for mouse messages */
\r
3653 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3657 static int recursive = 0;
\r
3659 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3662 if (message == WM_MBUTTONUP) {
\r
3663 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3664 to the middle button: we simulate pressing the left button too!
\r
3666 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3667 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3673 pt.x = LOWORD(lParam);
\r
3674 pt.y = HIWORD(lParam);
\r
3675 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3676 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3677 if (!flipView && y >= 0) {
\r
3678 y = BOARD_HEIGHT - 1 - y;
\r
3680 if (flipView && x >= 0) {
\r
3681 x = BOARD_WIDTH - 1 - x;
\r
3684 switch (message) {
\r
3685 case WM_LBUTTONDOWN:
\r
3686 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3687 if (gameMode == EditPosition) {
\r
3688 SetWhiteToPlayEvent();
\r
3689 } else if (gameMode == IcsPlayingBlack ||
\r
3690 gameMode == MachinePlaysWhite) {
\r
3692 } else if (gameMode == EditGame) {
\r
3693 AdjustClock(flipClock, -1);
\r
3695 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3696 if (gameMode == EditPosition) {
\r
3697 SetBlackToPlayEvent();
\r
3698 } else if (gameMode == IcsPlayingWhite ||
\r
3699 gameMode == MachinePlaysBlack) {
\r
3701 } else if (gameMode == EditGame) {
\r
3702 AdjustClock(!flipClock, -1);
\r
3705 dragInfo.start.x = dragInfo.start.y = -1;
\r
3706 dragInfo.from = dragInfo.start;
\r
3707 if(fromX == -1 && frozen) { // not sure where this is for
\r
3708 fromX = fromY = -1;
\r
3709 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3712 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3713 DrawPosition(TRUE, NULL);
\r
3716 case WM_LBUTTONUP:
\r
3717 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3718 DrawPosition(TRUE, NULL);
\r
3721 case WM_MOUSEMOVE:
\r
3722 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3723 if ((appData.animateDragging || appData.highlightDragging)
\r
3724 && (wParam & MK_LBUTTON)
\r
3725 && dragInfo.from.x >= 0)
\r
3727 BOOL full_repaint = FALSE;
\r
3729 if (appData.animateDragging) {
\r
3730 dragInfo.pos = pt;
\r
3732 if (appData.highlightDragging) {
\r
3733 SetHighlights(fromX, fromY, x, y);
\r
3734 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3735 full_repaint = TRUE;
\r
3739 DrawPosition( full_repaint, NULL);
\r
3741 dragInfo.lastpos = dragInfo.pos;
\r
3745 case WM_MOUSEWHEEL: // [DM]
\r
3746 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3747 /* Mouse Wheel is being rolled forward
\r
3748 * Play moves forward
\r
3750 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3751 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3752 /* Mouse Wheel is being rolled backward
\r
3753 * Play moves backward
\r
3755 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3756 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3760 case WM_MBUTTONUP:
\r
3761 case WM_RBUTTONUP:
\r
3763 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3766 case WM_MBUTTONDOWN:
\r
3767 case WM_RBUTTONDOWN:
\r
3770 fromX = fromY = -1;
\r
3771 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3772 dragInfo.start.x = dragInfo.start.y = -1;
\r
3773 dragInfo.from = dragInfo.start;
\r
3774 dragInfo.lastpos = dragInfo.pos;
\r
3775 if (appData.highlightDragging) {
\r
3776 ClearHighlights();
\r
3779 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3780 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3781 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3782 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3783 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3786 DrawPosition(TRUE, NULL);
\r
3788 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3791 if (message == WM_MBUTTONDOWN) {
\r
3792 buttonCount = 3; /* even if system didn't think so */
\r
3793 if (wParam & MK_SHIFT)
\r
3794 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3796 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3797 } else { /* message == WM_RBUTTONDOWN */
\r
3798 /* Just have one menu, on the right button. Windows users don't
\r
3799 think to try the middle one, and sometimes other software steals
\r
3800 it, or it doesn't really exist. */
\r
3801 if(gameInfo.variant != VariantShogi)
\r
3802 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3804 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3808 SetCapture(hwndMain);
3811 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3812 SetupDropMenu(hmenu);
\r
3813 MenuPopup(hwnd, pt, hmenu, -1);
\r
3823 /* Preprocess messages for buttons in main window */
\r
3825 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3827 int id = GetWindowLong(hwnd, GWL_ID);
\r
3830 for (i=0; i<N_BUTTONS; i++) {
\r
3831 if (buttonDesc[i].id == id) break;
\r
3833 if (i == N_BUTTONS) return 0;
\r
3834 switch (message) {
\r
3839 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3840 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3847 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3850 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3851 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3852 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3853 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3855 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3857 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3858 PopUpMoveDialog((char)wParam);
\r
3864 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3867 /* Process messages for Promotion dialog box */
\r
3869 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3873 switch (message) {
\r
3874 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3875 /* Center the dialog over the application window */
\r
3876 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3877 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3878 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3879 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3880 SW_SHOW : SW_HIDE);
\r
3881 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3882 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3883 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3884 PieceToChar(WhiteAngel) != '~') ||
\r
3885 (PieceToChar(BlackAngel) >= 'A' &&
\r
3886 PieceToChar(BlackAngel) != '~') ) ?
\r
3887 SW_SHOW : SW_HIDE);
\r
3888 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3889 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3890 PieceToChar(WhiteMarshall) != '~') ||
\r
3891 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3892 PieceToChar(BlackMarshall) != '~') ) ?
\r
3893 SW_SHOW : SW_HIDE);
\r
3894 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3895 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3896 gameInfo.variant != VariantShogi ?
\r
3897 SW_SHOW : SW_HIDE);
\r
3898 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3899 gameInfo.variant != VariantShogi ?
\r
3900 SW_SHOW : SW_HIDE);
\r
3901 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3902 gameInfo.variant == VariantShogi ?
\r
3903 SW_SHOW : SW_HIDE);
\r
3904 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3905 gameInfo.variant == VariantShogi ?
\r
3906 SW_SHOW : SW_HIDE);
\r
3907 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3908 gameInfo.variant == VariantSuper ?
\r
3909 SW_SHOW : SW_HIDE);
\r
3912 case WM_COMMAND: /* message: received a command */
\r
3913 switch (LOWORD(wParam)) {
\r
3915 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3916 ClearHighlights();
\r
3917 DrawPosition(FALSE, NULL);
\r
3920 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3923 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3926 promoChar = PieceToChar(BlackRook);
\r
3929 promoChar = PieceToChar(BlackBishop);
\r
3931 case PB_Chancellor:
\r
3932 promoChar = PieceToChar(BlackMarshall);
\r
3934 case PB_Archbishop:
\r
3935 promoChar = PieceToChar(BlackAngel);
\r
3938 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
3943 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3944 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
3945 only show the popup when we are already sure the move is valid or
\r
3946 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
3947 will figure out it is a promotion from the promoChar. */
\r
3948 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
3949 fromX = fromY = -1;
\r
3950 if (!appData.highlightLastMove) {
\r
3951 ClearHighlights();
\r
3952 DrawPosition(FALSE, NULL);
\r
3959 /* Pop up promotion dialog */
\r
3961 PromotionPopup(HWND hwnd)
\r
3965 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
3966 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
3967 hwnd, (DLGPROC)lpProc);
\r
3968 FreeProcInstance(lpProc);
\r
3974 DrawPosition(TRUE, NULL);
\r
3975 PromotionPopup(hwndMain);
\r
3978 /* Toggle ShowThinking */
\r
3980 ToggleShowThinking()
\r
3982 appData.showThinking = !appData.showThinking;
\r
3983 ShowThinkingEvent();
\r
3987 LoadGameDialog(HWND hwnd, char* title)
\r
3991 char fileTitle[MSG_SIZ];
\r
3992 f = OpenFileDialog(hwnd, "rb", "",
\r
3993 appData.oldSaveStyle ? "gam" : "pgn",
\r
3995 title, &number, fileTitle, NULL);
\r
3997 cmailMsgLoaded = FALSE;
\r
3998 if (number == 0) {
\r
3999 int error = GameListBuild(f);
\r
4001 DisplayError("Cannot build game list", error);
\r
4002 } else if (!ListEmpty(&gameList) &&
\r
4003 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4004 GameListPopUp(f, fileTitle);
\r
4007 GameListDestroy();
\r
4010 LoadGame(f, number, fileTitle, FALSE);
\r
4014 int get_term_width()
\r
4019 HFONT hfont, hold_font;
\r
4024 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4028 // get the text metrics
\r
4029 hdc = GetDC(hText);
\r
4030 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4031 if (consoleCF.dwEffects & CFE_BOLD)
\r
4032 lf.lfWeight = FW_BOLD;
\r
4033 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4034 lf.lfItalic = TRUE;
\r
4035 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4036 lf.lfStrikeOut = TRUE;
\r
4037 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4038 lf.lfUnderline = TRUE;
\r
4039 hfont = CreateFontIndirect(&lf);
\r
4040 hold_font = SelectObject(hdc, hfont);
\r
4041 GetTextMetrics(hdc, &tm);
\r
4042 SelectObject(hdc, hold_font);
\r
4043 DeleteObject(hfont);
\r
4044 ReleaseDC(hText, hdc);
\r
4046 // get the rectangle
\r
4047 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4049 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4052 void UpdateICSWidth(HWND hText)
\r
4054 LONG old_width, new_width;
\r
4056 new_width = get_term_width(hText, FALSE);
\r
4057 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4058 if (new_width != old_width)
\r
4060 ics_update_width(new_width);
\r
4061 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4066 ChangedConsoleFont()
\r
4069 CHARRANGE tmpsel, sel;
\r
4070 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4071 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4072 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4075 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4076 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4077 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4078 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4079 * size. This was undocumented in the version of MSVC++ that I had
\r
4080 * when I wrote the code, but is apparently documented now.
\r
4082 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4083 cfmt.bCharSet = f->lf.lfCharSet;
\r
4084 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4085 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4086 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4087 /* Why are the following seemingly needed too? */
\r
4088 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4089 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4090 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4092 tmpsel.cpMax = -1; /*999999?*/
\r
4093 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4094 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4095 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4096 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4098 paraf.cbSize = sizeof(paraf);
\r
4099 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4100 paraf.dxStartIndent = 0;
\r
4101 paraf.dxOffset = WRAP_INDENT;
\r
4102 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4103 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4104 UpdateICSWidth(hText);
\r
4107 /*---------------------------------------------------------------------------*\
\r
4109 * Window Proc for main window
\r
4111 \*---------------------------------------------------------------------------*/
\r
4113 /* Process messages for main window, etc. */
\r
4115 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4118 int wmId, wmEvent;
\r
4122 char fileTitle[MSG_SIZ];
\r
4123 char buf[MSG_SIZ];
\r
4124 static SnapData sd;
\r
4126 switch (message) {
\r
4128 case WM_PAINT: /* message: repaint portion of window */
\r
4132 case WM_ERASEBKGND:
\r
4133 if (IsIconic(hwnd)) {
\r
4134 /* Cheat; change the message */
\r
4135 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4137 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4141 case WM_LBUTTONDOWN:
\r
4142 case WM_MBUTTONDOWN:
\r
4143 case WM_RBUTTONDOWN:
\r
4144 case WM_LBUTTONUP:
\r
4145 case WM_MBUTTONUP:
\r
4146 case WM_RBUTTONUP:
\r
4147 case WM_MOUSEMOVE:
\r
4148 case WM_MOUSEWHEEL:
\r
4149 MouseEvent(hwnd, message, wParam, lParam);
\r
4152 JAWS_KB_NAVIGATION
\r
4156 JAWS_ALT_INTERCEPT
\r
4158 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4159 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4160 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4161 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4163 SendMessage(h, message, wParam, lParam);
\r
4164 } else if(lParam != KF_REPEAT) {
\r
4165 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4166 PopUpMoveDialog((char)wParam);
\r
4167 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4168 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4173 case WM_PALETTECHANGED:
\r
4174 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4176 HDC hdc = GetDC(hwndMain);
\r
4177 SelectPalette(hdc, hPal, TRUE);
\r
4178 nnew = RealizePalette(hdc);
\r
4180 paletteChanged = TRUE;
\r
4181 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4183 ReleaseDC(hwnd, hdc);
\r
4187 case WM_QUERYNEWPALETTE:
\r
4188 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4190 HDC hdc = GetDC(hwndMain);
\r
4191 paletteChanged = FALSE;
\r
4192 SelectPalette(hdc, hPal, FALSE);
\r
4193 nnew = RealizePalette(hdc);
\r
4195 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4197 ReleaseDC(hwnd, hdc);
\r
4202 case WM_COMMAND: /* message: command from application menu */
\r
4203 wmId = LOWORD(wParam);
\r
4204 wmEvent = HIWORD(wParam);
\r
4209 SAY("new game enter a move to play against the computer with white");
\r
4212 case IDM_NewGameFRC:
\r
4213 if( NewGameFRC() == 0 ) {
\r
4218 case IDM_NewVariant:
\r
4219 NewVariantPopup(hwnd);
\r
4222 case IDM_LoadGame:
\r
4223 LoadGameDialog(hwnd, "Load Game from File");
\r
4226 case IDM_LoadNextGame:
\r
4230 case IDM_LoadPrevGame:
\r
4234 case IDM_ReloadGame:
\r
4238 case IDM_LoadPosition:
\r
4239 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4240 Reset(FALSE, TRUE);
\r
4243 f = OpenFileDialog(hwnd, "rb", "",
\r
4244 appData.oldSaveStyle ? "pos" : "fen",
\r
4246 "Load Position from File", &number, fileTitle, NULL);
\r
4248 LoadPosition(f, number, fileTitle);
\r
4252 case IDM_LoadNextPosition:
\r
4253 ReloadPosition(1);
\r
4256 case IDM_LoadPrevPosition:
\r
4257 ReloadPosition(-1);
\r
4260 case IDM_ReloadPosition:
\r
4261 ReloadPosition(0);
\r
4264 case IDM_SaveGame:
\r
4265 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4266 f = OpenFileDialog(hwnd, "a", defName,
\r
4267 appData.oldSaveStyle ? "gam" : "pgn",
\r
4269 "Save Game to File", NULL, fileTitle, NULL);
\r
4271 SaveGame(f, 0, "");
\r
4275 case IDM_SavePosition:
\r
4276 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4277 f = OpenFileDialog(hwnd, "a", defName,
\r
4278 appData.oldSaveStyle ? "pos" : "fen",
\r
4280 "Save Position to File", NULL, fileTitle, NULL);
\r
4282 SavePosition(f, 0, "");
\r
4286 case IDM_SaveDiagram:
\r
4287 defName = "diagram";
\r
4288 f = OpenFileDialog(hwnd, "wb", defName,
\r
4291 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4297 case IDM_CopyGame:
\r
4298 CopyGameToClipboard();
\r
4301 case IDM_PasteGame:
\r
4302 PasteGameFromClipboard();
\r
4305 case IDM_CopyGameListToClipboard:
\r
4306 CopyGameListToClipboard();
\r
4309 /* [AS] Autodetect FEN or PGN data */
\r
4310 case IDM_PasteAny:
\r
4311 PasteGameOrFENFromClipboard();
\r
4314 /* [AS] Move history */
\r
4315 case IDM_ShowMoveHistory:
\r
4316 if( MoveHistoryIsUp() ) {
\r
4317 MoveHistoryPopDown();
\r
4320 MoveHistoryPopUp();
\r
4324 /* [AS] Eval graph */
\r
4325 case IDM_ShowEvalGraph:
\r
4326 if( EvalGraphIsUp() ) {
\r
4327 EvalGraphPopDown();
\r
4331 SetFocus(hwndMain);
\r
4335 /* [AS] Engine output */
\r
4336 case IDM_ShowEngineOutput:
\r
4337 if( EngineOutputIsUp() ) {
\r
4338 EngineOutputPopDown();
\r
4341 EngineOutputPopUp();
\r
4345 /* [AS] User adjudication */
\r
4346 case IDM_UserAdjudication_White:
\r
4347 UserAdjudicationEvent( +1 );
\r
4350 case IDM_UserAdjudication_Black:
\r
4351 UserAdjudicationEvent( -1 );
\r
4354 case IDM_UserAdjudication_Draw:
\r
4355 UserAdjudicationEvent( 0 );
\r
4358 /* [AS] Game list options dialog */
\r
4359 case IDM_GameListOptions:
\r
4360 GameListOptions();
\r
4367 case IDM_CopyPosition:
\r
4368 CopyFENToClipboard();
\r
4371 case IDM_PastePosition:
\r
4372 PasteFENFromClipboard();
\r
4375 case IDM_MailMove:
\r
4379 case IDM_ReloadCMailMsg:
\r
4380 Reset(TRUE, TRUE);
\r
4381 ReloadCmailMsgEvent(FALSE);
\r
4384 case IDM_Minimize:
\r
4385 ShowWindow(hwnd, SW_MINIMIZE);
\r
4392 case IDM_MachineWhite:
\r
4393 MachineWhiteEvent();
\r
4395 * refresh the tags dialog only if it's visible
\r
4397 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4399 tags = PGNTags(&gameInfo);
\r
4400 TagsPopUp(tags, CmailMsg());
\r
4403 SAY("computer starts playing white");
\r
4406 case IDM_MachineBlack:
\r
4407 MachineBlackEvent();
\r
4409 * refresh the tags dialog only if it's visible
\r
4411 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4413 tags = PGNTags(&gameInfo);
\r
4414 TagsPopUp(tags, CmailMsg());
\r
4417 SAY("computer starts playing black");
\r
4420 case IDM_TwoMachines:
\r
4421 TwoMachinesEvent();
\r
4423 * refresh the tags dialog only if it's visible
\r
4425 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4427 tags = PGNTags(&gameInfo);
\r
4428 TagsPopUp(tags, CmailMsg());
\r
4431 SAY("programs start playing each other");
\r
4434 case IDM_AnalysisMode:
\r
4435 if (!first.analysisSupport) {
\r
4436 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4437 DisplayError(buf, 0);
\r
4439 SAY("analyzing current position");
\r
4440 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4441 if (appData.icsActive) {
\r
4442 if (gameMode != IcsObserving) {
\r
4443 sprintf(buf, "You are not observing a game");
\r
4444 DisplayError(buf, 0);
\r
4445 /* secure check */
\r
4446 if (appData.icsEngineAnalyze) {
\r
4447 if (appData.debugMode)
\r
4448 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4449 ExitAnalyzeMode();
\r
4455 /* if enable, user want disable icsEngineAnalyze */
\r
4456 if (appData.icsEngineAnalyze) {
\r
4457 ExitAnalyzeMode();
\r
4461 appData.icsEngineAnalyze = TRUE;
\r
4462 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4465 if (!appData.showThinking) ToggleShowThinking();
\r
4466 AnalyzeModeEvent();
\r
4470 case IDM_AnalyzeFile:
\r
4471 if (!first.analysisSupport) {
\r
4472 char buf[MSG_SIZ];
\r
4473 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4474 DisplayError(buf, 0);
\r
4476 if (!appData.showThinking) ToggleShowThinking();
\r
4477 AnalyzeFileEvent();
\r
4478 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4479 AnalysisPeriodicEvent(1);
\r
4483 case IDM_IcsClient:
\r
4487 case IDM_EditGame:
\r
4492 case IDM_EditPosition:
\r
4493 EditPositionEvent();
\r
4494 SAY("to set up a position type a FEN");
\r
4497 case IDM_Training:
\r
4501 case IDM_ShowGameList:
\r
4502 ShowGameListProc();
\r
4505 case IDM_EditTags:
\r
4509 case IDM_EditComment:
\r
4510 if (commentUp && editComment) {
\r
4513 EditCommentEvent();
\r
4533 case IDM_CallFlag:
\r
4553 case IDM_StopObserving:
\r
4554 StopObservingEvent();
\r
4557 case IDM_StopExamining:
\r
4558 StopExaminingEvent();
\r
4561 case IDM_TypeInMove:
\r
4562 PopUpMoveDialog('\000');
\r
4565 case IDM_TypeInName:
\r
4566 PopUpNameDialog('\000');
\r
4569 case IDM_Backward:
\r
4571 SetFocus(hwndMain);
\r
4578 SetFocus(hwndMain);
\r
4583 SetFocus(hwndMain);
\r
4588 SetFocus(hwndMain);
\r
4595 case IDM_TruncateGame:
\r
4596 TruncateGameEvent();
\r
4603 case IDM_RetractMove:
\r
4604 RetractMoveEvent();
\r
4607 case IDM_FlipView:
\r
4608 flipView = !flipView;
\r
4609 DrawPosition(FALSE, NULL);
\r
4612 case IDM_FlipClock:
\r
4613 flipClock = !flipClock;
\r
4614 DisplayBothClocks();
\r
4615 DrawPosition(FALSE, NULL);
\r
4618 case IDM_MuteSounds:
\r
4619 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4620 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4621 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4624 case IDM_GeneralOptions:
\r
4625 GeneralOptionsPopup(hwnd);
\r
4626 DrawPosition(TRUE, NULL);
\r
4629 case IDM_BoardOptions:
\r
4630 BoardOptionsPopup(hwnd);
\r
4633 case IDM_EnginePlayOptions:
\r
4634 EnginePlayOptionsPopup(hwnd);
\r
4637 case IDM_Engine1Options:
\r
4638 EngineOptionsPopup(hwnd, &first);
\r
4641 case IDM_Engine2Options:
\r
4642 EngineOptionsPopup(hwnd, &second);
\r
4645 case IDM_OptionsUCI:
\r
4646 UciOptionsPopup(hwnd);
\r
4649 case IDM_IcsOptions:
\r
4650 IcsOptionsPopup(hwnd);
\r
4654 FontsOptionsPopup(hwnd);
\r
4658 SoundOptionsPopup(hwnd);
\r
4661 case IDM_CommPort:
\r
4662 CommPortOptionsPopup(hwnd);
\r
4665 case IDM_LoadOptions:
\r
4666 LoadOptionsPopup(hwnd);
\r
4669 case IDM_SaveOptions:
\r
4670 SaveOptionsPopup(hwnd);
\r
4673 case IDM_TimeControl:
\r
4674 TimeControlOptionsPopup(hwnd);
\r
4677 case IDM_SaveSettings:
\r
4678 SaveSettings(settingsFileName);
\r
4681 case IDM_SaveSettingsOnExit:
\r
4682 saveSettingsOnExit = !saveSettingsOnExit;
\r
4683 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4684 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4685 MF_CHECKED : MF_UNCHECKED));
\r
4696 case IDM_AboutGame:
\r
4701 appData.debugMode = !appData.debugMode;
\r
4702 if (appData.debugMode) {
\r
4703 char dir[MSG_SIZ];
\r
4704 GetCurrentDirectory(MSG_SIZ, dir);
\r
4705 SetCurrentDirectory(installDir);
\r
4706 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4707 SetCurrentDirectory(dir);
\r
4708 setbuf(debugFP, NULL);
\r
4715 case IDM_HELPCONTENTS:
\r
4716 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4717 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4718 MessageBox (GetFocus(),
\r
4719 "Unable to activate help",
\r
4720 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4724 case IDM_HELPSEARCH:
\r
4725 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4726 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4727 MessageBox (GetFocus(),
\r
4728 "Unable to activate help",
\r
4729 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4733 case IDM_HELPHELP:
\r
4734 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4735 MessageBox (GetFocus(),
\r
4736 "Unable to activate help",
\r
4737 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4742 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4744 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4745 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4746 FreeProcInstance(lpProc);
\r
4749 case IDM_DirectCommand1:
\r
4750 AskQuestionEvent("Direct Command",
\r
4751 "Send to chess program:", "", "1");
\r
4753 case IDM_DirectCommand2:
\r
4754 AskQuestionEvent("Direct Command",
\r
4755 "Send to second chess program:", "", "2");
\r
4758 case EP_WhitePawn:
\r
4759 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4760 fromX = fromY = -1;
\r
4763 case EP_WhiteKnight:
\r
4764 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4765 fromX = fromY = -1;
\r
4768 case EP_WhiteBishop:
\r
4769 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4770 fromX = fromY = -1;
\r
4773 case EP_WhiteRook:
\r
4774 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4775 fromX = fromY = -1;
\r
4778 case EP_WhiteQueen:
\r
4779 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4780 fromX = fromY = -1;
\r
4783 case EP_WhiteFerz:
\r
4784 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4785 fromX = fromY = -1;
\r
4788 case EP_WhiteWazir:
\r
4789 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4790 fromX = fromY = -1;
\r
4793 case EP_WhiteAlfil:
\r
4794 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4795 fromX = fromY = -1;
\r
4798 case EP_WhiteCannon:
\r
4799 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4800 fromX = fromY = -1;
\r
4803 case EP_WhiteCardinal:
\r
4804 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4805 fromX = fromY = -1;
\r
4808 case EP_WhiteMarshall:
\r
4809 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4810 fromX = fromY = -1;
\r
4813 case EP_WhiteKing:
\r
4814 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4815 fromX = fromY = -1;
\r
4818 case EP_BlackPawn:
\r
4819 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4820 fromX = fromY = -1;
\r
4823 case EP_BlackKnight:
\r
4824 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4825 fromX = fromY = -1;
\r
4828 case EP_BlackBishop:
\r
4829 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4830 fromX = fromY = -1;
\r
4833 case EP_BlackRook:
\r
4834 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4835 fromX = fromY = -1;
\r
4838 case EP_BlackQueen:
\r
4839 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4840 fromX = fromY = -1;
\r
4843 case EP_BlackFerz:
\r
4844 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4845 fromX = fromY = -1;
\r
4848 case EP_BlackWazir:
\r
4849 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4850 fromX = fromY = -1;
\r
4853 case EP_BlackAlfil:
\r
4854 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4855 fromX = fromY = -1;
\r
4858 case EP_BlackCannon:
\r
4859 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4860 fromX = fromY = -1;
\r
4863 case EP_BlackCardinal:
\r
4864 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4865 fromX = fromY = -1;
\r
4868 case EP_BlackMarshall:
\r
4869 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4870 fromX = fromY = -1;
\r
4873 case EP_BlackKing:
\r
4874 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4875 fromX = fromY = -1;
\r
4878 case EP_EmptySquare:
\r
4879 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4880 fromX = fromY = -1;
\r
4883 case EP_ClearBoard:
\r
4884 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4885 fromX = fromY = -1;
\r
4889 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4890 fromX = fromY = -1;
\r
4894 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4895 fromX = fromY = -1;
\r
4899 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4900 fromX = fromY = -1;
\r
4904 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4905 fromX = fromY = -1;
\r
4909 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4910 fromX = fromY = -1;
\r
4914 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4915 fromX = fromY = -1;
\r
4919 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4920 fromX = fromY = -1;
\r
4924 DropMenuEvent(WhiteRook, fromX, fromY);
\r
4925 fromX = fromY = -1;
\r
4929 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
4930 fromX = fromY = -1;
\r
4934 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4940 case CLOCK_TIMER_ID:
\r
4941 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
4942 clockTimerEvent = 0;
\r
4943 DecrementClocks(); /* call into back end */
\r
4945 case LOAD_GAME_TIMER_ID:
\r
4946 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
4947 loadGameTimerEvent = 0;
\r
4948 AutoPlayGameLoop(); /* call into back end */
\r
4950 case ANALYSIS_TIMER_ID:
\r
4951 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
4952 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
4953 AnalysisPeriodicEvent(0);
\r
4955 KillTimer(hwnd, analysisTimerEvent);
\r
4956 analysisTimerEvent = 0;
\r
4959 case DELAYED_TIMER_ID:
\r
4960 KillTimer(hwnd, delayedTimerEvent);
\r
4961 delayedTimerEvent = 0;
\r
4962 delayedTimerCallback();
\r
4967 case WM_USER_Input:
\r
4968 InputEvent(hwnd, message, wParam, lParam);
\r
4971 /* [AS] Also move "attached" child windows */
\r
4972 case WM_WINDOWPOSCHANGING:
\r
4974 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
4975 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
4977 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
4978 /* Window is moving */
\r
4981 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
4982 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
4983 rcMain.right = wpMain.x + wpMain.width;
\r
4984 rcMain.top = wpMain.y;
\r
4985 rcMain.bottom = wpMain.y + wpMain.height;
\r
4987 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
4988 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
4989 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
4990 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
4991 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
4992 wpMain.x = lpwp->x;
\r
4993 wpMain.y = lpwp->y;
\r
4998 /* [AS] Snapping */
\r
4999 case WM_ENTERSIZEMOVE:
\r
5000 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5001 if (hwnd == hwndMain) {
\r
5002 doingSizing = TRUE;
\r
5005 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5009 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5010 if (hwnd == hwndMain) {
\r
5011 lastSizing = wParam;
\r
5016 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5017 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5019 case WM_EXITSIZEMOVE:
\r
5020 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5021 if (hwnd == hwndMain) {
\r
5023 doingSizing = FALSE;
\r
5024 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5025 GetClientRect(hwnd, &client);
\r
5026 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5028 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5030 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5033 case WM_DESTROY: /* message: window being destroyed */
\r
5034 PostQuitMessage(0);
\r
5038 if (hwnd == hwndMain) {
\r
5043 default: /* Passes it on if unprocessed */
\r
5044 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5049 /*---------------------------------------------------------------------------*\
\r
5051 * Misc utility routines
\r
5053 \*---------------------------------------------------------------------------*/
\r
5056 * Decent random number generator, at least not as bad as Windows
\r
5057 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5059 unsigned int randstate;
\r
5064 randstate = randstate * 1664525 + 1013904223;
\r
5065 return (int) randstate & 0x7fffffff;
\r
5069 mysrandom(unsigned int seed)
\r
5076 * returns TRUE if user selects a different color, FALSE otherwise
\r
5080 ChangeColor(HWND hwnd, COLORREF *which)
\r
5082 static BOOL firstTime = TRUE;
\r
5083 static DWORD customColors[16];
\r
5085 COLORREF newcolor;
\r
5090 /* Make initial colors in use available as custom colors */
\r
5091 /* Should we put the compiled-in defaults here instead? */
\r
5093 customColors[i++] = lightSquareColor & 0xffffff;
\r
5094 customColors[i++] = darkSquareColor & 0xffffff;
\r
5095 customColors[i++] = whitePieceColor & 0xffffff;
\r
5096 customColors[i++] = blackPieceColor & 0xffffff;
\r
5097 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5098 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5100 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5101 customColors[i++] = textAttribs[ccl].color;
\r
5103 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5104 firstTime = FALSE;
\r
5107 cc.lStructSize = sizeof(cc);
\r
5108 cc.hwndOwner = hwnd;
\r
5109 cc.hInstance = NULL;
\r
5110 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5111 cc.lpCustColors = (LPDWORD) customColors;
\r
5112 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5114 if (!ChooseColor(&cc)) return FALSE;
\r
5116 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5117 if (newcolor == *which) return FALSE;
\r
5118 *which = newcolor;
\r
5122 InitDrawingColors();
\r
5123 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5128 MyLoadSound(MySound *ms)
\r
5134 if (ms->data) free(ms->data);
\r
5137 switch (ms->name[0]) {
\r
5143 /* System sound from Control Panel. Don't preload here. */
\r
5147 if (ms->name[1] == NULLCHAR) {
\r
5148 /* "!" alone = silence */
\r
5151 /* Builtin wave resource. Error if not found. */
\r
5152 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5153 if (h == NULL) break;
\r
5154 ms->data = (void *)LoadResource(hInst, h);
\r
5155 if (h == NULL) break;
\r
5160 /* .wav file. Error if not found. */
\r
5161 f = fopen(ms->name, "rb");
\r
5162 if (f == NULL) break;
\r
5163 if (fstat(fileno(f), &st) < 0) break;
\r
5164 ms->data = malloc(st.st_size);
\r
5165 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5171 char buf[MSG_SIZ];
\r
5172 sprintf(buf, "Error loading sound %s", ms->name);
\r
5173 DisplayError(buf, GetLastError());
\r
5179 MyPlaySound(MySound *ms)
\r
5181 BOOLEAN ok = FALSE;
\r
5183 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5184 switch (ms->name[0]) {
\r
5186 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5191 /* System sound from Control Panel (deprecated feature).
\r
5192 "$" alone or an unset sound name gets default beep (still in use). */
\r
5193 if (ms->name[1]) {
\r
5194 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5196 if (!ok) ok = MessageBeep(MB_OK);
\r
5199 /* Builtin wave resource, or "!" alone for silence */
\r
5200 if (ms->name[1]) {
\r
5201 if (ms->data == NULL) return FALSE;
\r
5202 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5208 /* .wav file. Error if not found. */
\r
5209 if (ms->data == NULL) return FALSE;
\r
5210 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5213 /* Don't print an error: this can happen innocently if the sound driver
\r
5214 is busy; for instance, if another instance of WinBoard is playing
\r
5215 a sound at about the same time. */
\r
5221 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5224 OPENFILENAME *ofn;
\r
5225 static UINT *number; /* gross that this is static */
\r
5227 switch (message) {
\r
5228 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5229 /* Center the dialog over the application window */
\r
5230 ofn = (OPENFILENAME *) lParam;
\r
5231 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5232 number = (UINT *) ofn->lCustData;
\r
5233 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5237 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5238 return FALSE; /* Allow for further processing */
\r
5241 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5242 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5244 return FALSE; /* Allow for further processing */
\r
5250 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5252 static UINT *number;
\r
5253 OPENFILENAME *ofname;
\r
5256 case WM_INITDIALOG:
\r
5257 ofname = (OPENFILENAME *)lParam;
\r
5258 number = (UINT *)(ofname->lCustData);
\r
5261 ofnot = (OFNOTIFY *)lParam;
\r
5262 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5263 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5272 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5273 char *nameFilt, char *dlgTitle, UINT *number,
\r
5274 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5276 OPENFILENAME openFileName;
\r
5277 char buf1[MSG_SIZ];
\r
5280 if (fileName == NULL) fileName = buf1;
\r
5281 if (defName == NULL) {
\r
5282 strcpy(fileName, "*.");
\r
5283 strcat(fileName, defExt);
\r
5285 strcpy(fileName, defName);
\r
5287 if (fileTitle) strcpy(fileTitle, "");
\r
5288 if (number) *number = 0;
\r
5290 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5291 openFileName.hwndOwner = hwnd;
\r
5292 openFileName.hInstance = (HANDLE) hInst;
\r
5293 openFileName.lpstrFilter = nameFilt;
\r
5294 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5295 openFileName.nMaxCustFilter = 0L;
\r
5296 openFileName.nFilterIndex = 1L;
\r
5297 openFileName.lpstrFile = fileName;
\r
5298 openFileName.nMaxFile = MSG_SIZ;
\r
5299 openFileName.lpstrFileTitle = fileTitle;
\r
5300 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5301 openFileName.lpstrInitialDir = NULL;
\r
5302 openFileName.lpstrTitle = dlgTitle;
\r
5303 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5304 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5305 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5306 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5307 openFileName.nFileOffset = 0;
\r
5308 openFileName.nFileExtension = 0;
\r
5309 openFileName.lpstrDefExt = defExt;
\r
5310 openFileName.lCustData = (LONG) number;
\r
5311 openFileName.lpfnHook = oldDialog ?
\r
5312 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5313 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5315 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5316 GetOpenFileName(&openFileName)) {
\r
5317 /* open the file */
\r
5318 f = fopen(openFileName.lpstrFile, write);
\r
5320 MessageBox(hwnd, "File open failed", NULL,
\r
5321 MB_OK|MB_ICONEXCLAMATION);
\r
5325 int err = CommDlgExtendedError();
\r
5326 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5335 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5337 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5340 * Get the first pop-up menu in the menu template. This is the
\r
5341 * menu that TrackPopupMenu displays.
\r
5343 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5345 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5348 * TrackPopup uses screen coordinates, so convert the
\r
5349 * coordinates of the mouse click to screen coordinates.
\r
5351 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5353 /* Draw and track the floating pop-up menu. */
\r
5354 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5355 pt.x, pt.y, 0, hwnd, NULL);
\r
5357 /* Destroy the menu.*/
\r
5358 DestroyMenu(hmenu);
\r
5363 int sizeX, sizeY, newSizeX, newSizeY;
\r
5365 } ResizeEditPlusButtonsClosure;
\r
5368 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5370 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5374 if (hChild == cl->hText) return TRUE;
\r
5375 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5376 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5377 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5378 ScreenToClient(cl->hDlg, &pt);
\r
5379 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5380 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5384 /* Resize a dialog that has a (rich) edit field filling most of
\r
5385 the top, with a row of buttons below */
\r
5387 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5390 int newTextHeight, newTextWidth;
\r
5391 ResizeEditPlusButtonsClosure cl;
\r
5393 /*if (IsIconic(hDlg)) return;*/
\r
5394 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5396 cl.hdwp = BeginDeferWindowPos(8);
\r
5398 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5399 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5400 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5401 if (newTextHeight < 0) {
\r
5402 newSizeY += -newTextHeight;
\r
5403 newTextHeight = 0;
\r
5405 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5406 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5412 cl.newSizeX = newSizeX;
\r
5413 cl.newSizeY = newSizeY;
\r
5414 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5416 EndDeferWindowPos(cl.hdwp);
\r
5419 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5421 RECT rChild, rParent;
\r
5422 int wChild, hChild, wParent, hParent;
\r
5423 int wScreen, hScreen, xNew, yNew;
\r
5426 /* Get the Height and Width of the child window */
\r
5427 GetWindowRect (hwndChild, &rChild);
\r
5428 wChild = rChild.right - rChild.left;
\r
5429 hChild = rChild.bottom - rChild.top;
\r
5431 /* Get the Height and Width of the parent window */
\r
5432 GetWindowRect (hwndParent, &rParent);
\r
5433 wParent = rParent.right - rParent.left;
\r
5434 hParent = rParent.bottom - rParent.top;
\r
5436 /* Get the display limits */
\r
5437 hdc = GetDC (hwndChild);
\r
5438 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5439 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5440 ReleaseDC(hwndChild, hdc);
\r
5442 /* Calculate new X position, then adjust for screen */
\r
5443 xNew = rParent.left + ((wParent - wChild) /2);
\r
5446 } else if ((xNew+wChild) > wScreen) {
\r
5447 xNew = wScreen - wChild;
\r
5450 /* Calculate new Y position, then adjust for screen */
\r
5452 yNew = rParent.top + ((hParent - hChild) /2);
\r
5455 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5460 } else if ((yNew+hChild) > hScreen) {
\r
5461 yNew = hScreen - hChild;
\r
5464 /* Set it, and return */
\r
5465 return SetWindowPos (hwndChild, NULL,
\r
5466 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5469 /* Center one window over another */
\r
5470 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5472 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5475 /*---------------------------------------------------------------------------*\
\r
5477 * Startup Dialog functions
\r
5479 \*---------------------------------------------------------------------------*/
\r
5481 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5483 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5485 while (*cd != NULL) {
\r
5486 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5492 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5494 char buf1[MAX_ARG_LEN];
\r
5497 if (str[0] == '@') {
\r
5498 FILE* f = fopen(str + 1, "r");
\r
5500 DisplayFatalError(str + 1, errno, 2);
\r
5503 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5505 buf1[len] = NULLCHAR;
\r
5509 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5512 char buf[MSG_SIZ];
\r
5513 char *end = strchr(str, '\n');
\r
5514 if (end == NULL) return;
\r
5515 memcpy(buf, str, end - str);
\r
5516 buf[end - str] = NULLCHAR;
\r
5517 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5523 SetStartupDialogEnables(HWND hDlg)
\r
5525 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5526 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5527 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5528 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5529 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5530 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5531 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5532 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5533 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5534 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5535 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5536 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5537 IsDlgButtonChecked(hDlg, OPT_View));
\r
5541 QuoteForFilename(char *filename)
\r
5543 int dquote, space;
\r
5544 dquote = strchr(filename, '"') != NULL;
\r
5545 space = strchr(filename, ' ') != NULL;
\r
5546 if (dquote || space) {
\r
5558 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5560 char buf[MSG_SIZ];
\r
5563 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5564 q = QuoteForFilename(nthcp);
\r
5565 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5566 if (*nthdir != NULLCHAR) {
\r
5567 q = QuoteForFilename(nthdir);
\r
5568 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5570 if (*nthcp == NULLCHAR) {
\r
5571 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5572 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5573 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5574 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5579 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5581 char buf[MSG_SIZ];
\r
5585 switch (message) {
\r
5586 case WM_INITDIALOG:
\r
5587 /* Center the dialog */
\r
5588 CenterWindow (hDlg, GetDesktopWindow());
\r
5589 /* Initialize the dialog items */
\r
5590 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5591 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5592 firstChessProgramNames);
\r
5593 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5594 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5595 secondChessProgramNames);
\r
5596 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5597 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5598 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5599 if (*appData.icsHelper != NULLCHAR) {
\r
5600 char *q = QuoteForFilename(appData.icsHelper);
\r
5601 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5603 if (*appData.icsHost == NULLCHAR) {
\r
5604 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5605 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5606 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5607 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5608 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5611 if (appData.icsActive) {
\r
5612 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5614 else if (appData.noChessProgram) {
\r
5615 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5618 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5621 SetStartupDialogEnables(hDlg);
\r
5625 switch (LOWORD(wParam)) {
\r
5627 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5628 strcpy(buf, "/fcp=");
\r
5629 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5631 ParseArgs(StringGet, &p);
\r
5632 strcpy(buf, "/scp=");
\r
5633 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5635 ParseArgs(StringGet, &p);
\r
5636 appData.noChessProgram = FALSE;
\r
5637 appData.icsActive = FALSE;
\r
5638 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5639 strcpy(buf, "/ics /icshost=");
\r
5640 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5642 ParseArgs(StringGet, &p);
\r
5643 if (appData.zippyPlay) {
\r
5644 strcpy(buf, "/fcp=");
\r
5645 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5647 ParseArgs(StringGet, &p);
\r
5649 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5650 appData.noChessProgram = TRUE;
\r
5651 appData.icsActive = FALSE;
\r
5653 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5654 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5657 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5658 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5660 ParseArgs(StringGet, &p);
\r
5662 EndDialog(hDlg, TRUE);
\r
5669 case IDM_HELPCONTENTS:
\r
5670 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5671 MessageBox (GetFocus(),
\r
5672 "Unable to activate help",
\r
5673 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5678 SetStartupDialogEnables(hDlg);
\r
5686 /*---------------------------------------------------------------------------*\
\r
5688 * About box dialog functions
\r
5690 \*---------------------------------------------------------------------------*/
\r
5692 /* Process messages for "About" dialog box */
\r
5694 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5696 switch (message) {
\r
5697 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5698 /* Center the dialog over the application window */
\r
5699 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5700 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5704 case WM_COMMAND: /* message: received a command */
\r
5705 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5706 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5707 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5715 /*---------------------------------------------------------------------------*\
\r
5717 * Comment Dialog functions
\r
5719 \*---------------------------------------------------------------------------*/
\r
5722 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5724 static HANDLE hwndText = NULL;
\r
5725 int len, newSizeX, newSizeY, flags;
\r
5726 static int sizeX, sizeY;
\r
5731 switch (message) {
\r
5732 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5733 /* Initialize the dialog items */
\r
5734 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5735 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5736 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5737 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5738 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5739 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5740 SetWindowText(hDlg, commentTitle);
\r
5741 if (editComment) {
\r
5742 SetFocus(hwndText);
\r
5744 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5746 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5747 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5748 MAKELPARAM(FALSE, 0));
\r
5749 /* Size and position the dialog */
\r
5750 if (!commentDialog) {
\r
5751 commentDialog = hDlg;
\r
5752 flags = SWP_NOZORDER;
\r
5753 GetClientRect(hDlg, &rect);
\r
5754 sizeX = rect.right;
\r
5755 sizeY = rect.bottom;
\r
5756 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5757 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5758 WINDOWPLACEMENT wp;
\r
5759 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5760 wp.length = sizeof(WINDOWPLACEMENT);
\r
5762 wp.showCmd = SW_SHOW;
\r
5763 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5764 wp.rcNormalPosition.left = wpComment.x;
\r
5765 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5766 wp.rcNormalPosition.top = wpComment.y;
\r
5767 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5768 SetWindowPlacement(hDlg, &wp);
\r
5770 GetClientRect(hDlg, &rect);
\r
5771 newSizeX = rect.right;
\r
5772 newSizeY = rect.bottom;
\r
5773 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5774 newSizeX, newSizeY);
\r
5781 case WM_COMMAND: /* message: received a command */
\r
5782 switch (LOWORD(wParam)) {
\r
5784 if (editComment) {
\r
5786 /* Read changed options from the dialog box */
\r
5787 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5788 len = GetWindowTextLength(hwndText);
\r
5789 str = (char *) malloc(len + 1);
\r
5790 GetWindowText(hwndText, str, len + 1);
\r
5799 ReplaceComment(commentIndex, str);
\r
5806 case OPT_CancelComment:
\r
5810 case OPT_ClearComment:
\r
5811 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5814 case OPT_EditComment:
\r
5815 EditCommentEvent();
\r
5824 newSizeX = LOWORD(lParam);
\r
5825 newSizeY = HIWORD(lParam);
\r
5826 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5831 case WM_GETMINMAXINFO:
\r
5832 /* Prevent resizing window too small */
\r
5833 mmi = (MINMAXINFO *) lParam;
\r
5834 mmi->ptMinTrackSize.x = 100;
\r
5835 mmi->ptMinTrackSize.y = 100;
\r
5842 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5847 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5849 if (str == NULL) str = "";
\r
5850 p = (char *) malloc(2 * strlen(str) + 2);
\r
5853 if (*str == '\n') *q++ = '\r';
\r
5857 if (commentText != NULL) free(commentText);
\r
5859 commentIndex = index;
\r
5860 commentTitle = title;
\r
5862 editComment = edit;
\r
5864 if (commentDialog) {
\r
5865 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5866 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5868 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5869 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5870 hwndMain, (DLGPROC)lpProc);
\r
5871 FreeProcInstance(lpProc);
\r
5877 /*---------------------------------------------------------------------------*\
\r
5879 * Type-in move dialog functions
\r
5881 \*---------------------------------------------------------------------------*/
\r
5884 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5886 char move[MSG_SIZ];
\r
5888 ChessMove moveType;
\r
5889 int fromX, fromY, toX, toY;
\r
5892 switch (message) {
\r
5893 case WM_INITDIALOG:
\r
5894 move[0] = (char) lParam;
\r
5895 move[1] = NULLCHAR;
\r
5896 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5897 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5898 SetWindowText(hInput, move);
\r
5900 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5904 switch (LOWORD(wParam)) {
\r
5906 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5907 { int n; Board board;
\r
5909 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5910 EditPositionPasteFEN(move);
\r
5911 EndDialog(hDlg, TRUE);
\r
5914 // [HGM] movenum: allow move number to be typed in any mode
\r
5915 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5917 EndDialog(hDlg, TRUE);
\r
5921 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5922 gameMode != Training) {
\r
5923 DisplayMoveError("Displayed move is not current");
\r
5925 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
5926 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5927 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
5928 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
5929 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5930 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
5931 if (gameMode != Training)
\r
5932 forwardMostMove = currentMove;
\r
5933 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
5935 DisplayMoveError("Could not parse move");
\r
5938 EndDialog(hDlg, TRUE);
\r
5941 EndDialog(hDlg, FALSE);
\r
5952 PopUpMoveDialog(char firstchar)
\r
5956 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
5957 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
5958 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
5959 gameMode == EditPosition || gameMode == IcsExamining ||
\r
5960 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
5961 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
5962 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
5963 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
5964 gameMode == Training) {
\r
5965 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
5966 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
5967 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
5968 FreeProcInstance(lpProc);
\r
5972 /*---------------------------------------------------------------------------*\
\r
5974 * Type-in name dialog functions
\r
5976 \*---------------------------------------------------------------------------*/
\r
5979 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5981 char move[MSG_SIZ];
\r
5984 switch (message) {
\r
5985 case WM_INITDIALOG:
\r
5986 move[0] = (char) lParam;
\r
5987 move[1] = NULLCHAR;
\r
5988 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5989 hInput = GetDlgItem(hDlg, OPT_Name);
\r
5990 SetWindowText(hInput, move);
\r
5992 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5996 switch (LOWORD(wParam)) {
\r
5998 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
5999 appData.userName = strdup(move);
\r
6002 EndDialog(hDlg, TRUE);
\r
6005 EndDialog(hDlg, FALSE);
\r
6016 PopUpNameDialog(char firstchar)
\r
6020 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6021 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6022 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6023 FreeProcInstance(lpProc);
\r
6026 /*---------------------------------------------------------------------------*\
\r
6030 \*---------------------------------------------------------------------------*/
\r
6032 /* Nonmodal error box */
\r
6033 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6034 WPARAM wParam, LPARAM lParam);
\r
6037 ErrorPopUp(char *title, char *content)
\r
6041 BOOLEAN modal = hwndMain == NULL;
\r
6059 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6060 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6063 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6065 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6066 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6067 hwndMain, (DLGPROC)lpProc);
\r
6068 FreeProcInstance(lpProc);
\r
6075 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6076 if (errorDialog == NULL) return;
\r
6077 DestroyWindow(errorDialog);
\r
6078 errorDialog = NULL;
\r
6079 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6083 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6088 switch (message) {
\r
6089 case WM_INITDIALOG:
\r
6090 GetWindowRect(hDlg, &rChild);
\r
6093 SetWindowPos(hDlg, NULL, rChild.left,
\r
6094 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6095 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6099 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6100 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6101 and it doesn't work when you resize the dialog.
\r
6102 For now, just give it a default position.
\r
6104 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6106 errorDialog = hDlg;
\r
6107 SetWindowText(hDlg, errorTitle);
\r
6108 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6109 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6113 switch (LOWORD(wParam)) {
\r
6116 if (errorDialog == hDlg) errorDialog = NULL;
\r
6117 DestroyWindow(hDlg);
\r
6129 HWND gothicDialog = NULL;
\r
6132 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6136 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6138 switch (message) {
\r
6139 case WM_INITDIALOG:
\r
6140 GetWindowRect(hDlg, &rChild);
\r
6142 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6146 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6147 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6148 and it doesn't work when you resize the dialog.
\r
6149 For now, just give it a default position.
\r
6151 gothicDialog = hDlg;
\r
6152 SetWindowText(hDlg, errorTitle);
\r
6153 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6154 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6158 switch (LOWORD(wParam)) {
\r
6161 if (errorDialog == hDlg) errorDialog = NULL;
\r
6162 DestroyWindow(hDlg);
\r
6174 GothicPopUp(char *title, VariantClass variant)
\r
6177 static char *lastTitle;
\r
6179 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6180 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6182 if(lastTitle != title && gothicDialog != NULL) {
\r
6183 DestroyWindow(gothicDialog);
\r
6184 gothicDialog = NULL;
\r
6186 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6187 title = lastTitle;
\r
6188 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6189 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6190 hwndMain, (DLGPROC)lpProc);
\r
6191 FreeProcInstance(lpProc);
\r
6196 /*---------------------------------------------------------------------------*\
\r
6198 * Ics Interaction console functions
\r
6200 \*---------------------------------------------------------------------------*/
\r
6202 #define HISTORY_SIZE 64
\r
6203 static char *history[HISTORY_SIZE];
\r
6204 int histIn = 0, histP = 0;
\r
6207 SaveInHistory(char *cmd)
\r
6209 if (history[histIn] != NULL) {
\r
6210 free(history[histIn]);
\r
6211 history[histIn] = NULL;
\r
6213 if (*cmd == NULLCHAR) return;
\r
6214 history[histIn] = StrSave(cmd);
\r
6215 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6216 if (history[histIn] != NULL) {
\r
6217 free(history[histIn]);
\r
6218 history[histIn] = NULL;
\r
6224 PrevInHistory(char *cmd)
\r
6227 if (histP == histIn) {
\r
6228 if (history[histIn] != NULL) free(history[histIn]);
\r
6229 history[histIn] = StrSave(cmd);
\r
6231 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6232 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6234 return history[histP];
\r
6240 if (histP == histIn) return NULL;
\r
6241 histP = (histP + 1) % HISTORY_SIZE;
\r
6242 return history[histP];
\r
6246 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6250 hmenu = LoadMenu(hInst, "TextMenu");
\r
6251 h = GetSubMenu(hmenu, 0);
\r
6253 if (strcmp(e->item, "-") == 0) {
\r
6254 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6256 if (e->item[0] == '|') {
\r
6257 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
\r
6258 IDM_CommandX + i, &e->item[1]);
\r
6260 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
\r
6269 WNDPROC consoleTextWindowProc;
\r
6272 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6274 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6275 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6279 SetWindowText(hInput, command);
\r
6281 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6283 sel.cpMin = 999999;
\r
6284 sel.cpMax = 999999;
\r
6285 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6290 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6291 if (sel.cpMin == sel.cpMax) {
\r
6292 /* Expand to surrounding word */
\r
6295 tr.chrg.cpMax = sel.cpMin;
\r
6296 tr.chrg.cpMin = --sel.cpMin;
\r
6297 if (sel.cpMin < 0) break;
\r
6298 tr.lpstrText = name;
\r
6299 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6300 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6304 tr.chrg.cpMin = sel.cpMax;
\r
6305 tr.chrg.cpMax = ++sel.cpMax;
\r
6306 tr.lpstrText = name;
\r
6307 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6308 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6311 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6312 MessageBeep(MB_ICONEXCLAMATION);
\r
6316 tr.lpstrText = name;
\r
6317 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6319 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6320 MessageBeep(MB_ICONEXCLAMATION);
\r
6323 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6326 sprintf(buf, "%s %s", command, name);
\r
6327 SetWindowText(hInput, buf);
\r
6328 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6330 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6331 SetWindowText(hInput, buf);
\r
6332 sel.cpMin = 999999;
\r
6333 sel.cpMax = 999999;
\r
6334 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6340 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6345 switch (message) {
\r
6347 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6350 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6353 sel.cpMin = 999999;
\r
6354 sel.cpMax = 999999;
\r
6355 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6356 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6361 if(wParam != '\022') {
\r
6362 if (wParam == '\t') {
\r
6363 if (GetKeyState(VK_SHIFT) < 0) {
\r
6365 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6366 if (buttonDesc[0].hwnd) {
\r
6367 SetFocus(buttonDesc[0].hwnd);
\r
6369 SetFocus(hwndMain);
\r
6373 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6376 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6377 JAWS_DELETE( SetFocus(hInput); )
\r
6378 SendMessage(hInput, message, wParam, lParam);
\r
6381 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6382 case WM_RBUTTONUP:
\r
6383 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6384 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6385 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6388 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6389 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6390 if (sel.cpMin == sel.cpMax) {
\r
6391 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6392 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6394 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6395 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6397 pt.x = LOWORD(lParam);
\r
6398 pt.y = HIWORD(lParam);
\r
6399 MenuPopup(hwnd, pt, hmenu, -1);
\r
6403 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6405 return SendMessage(hInput, message, wParam, lParam);
\r
6406 case WM_MBUTTONDOWN:
\r
6407 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6408 case WM_RBUTTONDOWN:
\r
6409 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6410 /* Move selection here if it was empty */
\r
6412 pt.x = LOWORD(lParam);
\r
6413 pt.y = HIWORD(lParam);
\r
6414 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6415 if (sel.cpMin == sel.cpMax) {
\r
6416 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6417 sel.cpMax = sel.cpMin;
\r
6418 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6420 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6424 switch (LOWORD(wParam)) {
\r
6425 case IDM_QuickPaste:
\r
6427 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6428 if (sel.cpMin == sel.cpMax) {
\r
6429 MessageBeep(MB_ICONEXCLAMATION);
\r
6432 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6433 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6434 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6439 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6442 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6445 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6449 int i = LOWORD(wParam) - IDM_CommandX;
\r
6450 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6451 icsTextMenuEntry[i].command != NULL) {
\r
6452 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6453 icsTextMenuEntry[i].getname,
\r
6454 icsTextMenuEntry[i].immediate);
\r
6462 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6465 WNDPROC consoleInputWindowProc;
\r
6468 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6470 char buf[MSG_SIZ];
\r
6472 static BOOL sendNextChar = FALSE;
\r
6473 static BOOL quoteNextChar = FALSE;
\r
6474 InputSource *is = consoleInputSource;
\r
6478 switch (message) {
\r
6480 if (!appData.localLineEditing || sendNextChar) {
\r
6481 is->buf[0] = (CHAR) wParam;
\r
6483 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6484 sendNextChar = FALSE;
\r
6487 if (quoteNextChar) {
\r
6488 buf[0] = (char) wParam;
\r
6489 buf[1] = NULLCHAR;
\r
6490 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6491 quoteNextChar = FALSE;
\r
6495 case '\r': /* Enter key */
\r
6496 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6497 if (consoleEcho) SaveInHistory(is->buf);
\r
6498 is->buf[is->count++] = '\n';
\r
6499 is->buf[is->count] = NULLCHAR;
\r
6500 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6501 if (consoleEcho) {
\r
6502 ConsoleOutput(is->buf, is->count, TRUE);
\r
6503 } else if (appData.localLineEditing) {
\r
6504 ConsoleOutput("\n", 1, TRUE);
\r
6507 case '\033': /* Escape key */
\r
6508 SetWindowText(hwnd, "");
\r
6509 cf.cbSize = sizeof(CHARFORMAT);
\r
6510 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6511 if (consoleEcho) {
\r
6512 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6514 cf.crTextColor = COLOR_ECHOOFF;
\r
6516 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6517 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6519 case '\t': /* Tab key */
\r
6520 if (GetKeyState(VK_SHIFT) < 0) {
\r
6522 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6525 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6526 if (buttonDesc[0].hwnd) {
\r
6527 SetFocus(buttonDesc[0].hwnd);
\r
6529 SetFocus(hwndMain);
\r
6533 case '\023': /* Ctrl+S */
\r
6534 sendNextChar = TRUE;
\r
6536 case '\021': /* Ctrl+Q */
\r
6537 quoteNextChar = TRUE;
\r
6547 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6548 p = PrevInHistory(buf);
\r
6550 SetWindowText(hwnd, p);
\r
6551 sel.cpMin = 999999;
\r
6552 sel.cpMax = 999999;
\r
6553 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6558 p = NextInHistory();
\r
6560 SetWindowText(hwnd, p);
\r
6561 sel.cpMin = 999999;
\r
6562 sel.cpMax = 999999;
\r
6563 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6569 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6573 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6577 case WM_MBUTTONDOWN:
\r
6578 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6579 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6581 case WM_RBUTTONUP:
\r
6582 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6583 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6584 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6588 hmenu = LoadMenu(hInst, "InputMenu");
\r
6589 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6590 if (sel.cpMin == sel.cpMax) {
\r
6591 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6592 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6594 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6595 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6597 pt.x = LOWORD(lParam);
\r
6598 pt.y = HIWORD(lParam);
\r
6599 MenuPopup(hwnd, pt, hmenu, -1);
\r
6603 switch (LOWORD(wParam)) {
\r
6605 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6607 case IDM_SelectAll:
\r
6609 sel.cpMax = -1; /*999999?*/
\r
6610 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6613 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6616 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6619 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6624 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6627 #define CO_MAX 100000
\r
6628 #define CO_TRIM 1000
\r
6631 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6633 static SnapData sd;
\r
6634 HWND hText, hInput;
\r
6636 static int sizeX, sizeY;
\r
6637 int newSizeX, newSizeY;
\r
6641 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6642 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6644 switch (message) {
\r
6646 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6648 ENLINK *pLink = (ENLINK*)lParam;
\r
6649 if (pLink->msg == WM_LBUTTONUP)
\r
6653 tr.chrg = pLink->chrg;
\r
6654 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6655 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6656 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6657 free(tr.lpstrText);
\r
6661 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6662 hwndConsole = hDlg;
\r
6664 consoleTextWindowProc = (WNDPROC)
\r
6665 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6666 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6667 consoleInputWindowProc = (WNDPROC)
\r
6668 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6669 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6670 Colorize(ColorNormal, TRUE);
\r
6671 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6672 ChangedConsoleFont();
\r
6673 GetClientRect(hDlg, &rect);
\r
6674 sizeX = rect.right;
\r
6675 sizeY = rect.bottom;
\r
6676 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6677 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6678 WINDOWPLACEMENT wp;
\r
6679 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6680 wp.length = sizeof(WINDOWPLACEMENT);
\r
6682 wp.showCmd = SW_SHOW;
\r
6683 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6684 wp.rcNormalPosition.left = wpConsole.x;
\r
6685 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6686 wp.rcNormalPosition.top = wpConsole.y;
\r
6687 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6688 SetWindowPlacement(hDlg, &wp);
\r
6691 // [HGM] Chessknight's change 2004-07-13
\r
6692 else { /* Determine Defaults */
\r
6693 WINDOWPLACEMENT wp;
\r
6694 wpConsole.x = wpMain.width + 1;
\r
6695 wpConsole.y = wpMain.y;
\r
6696 wpConsole.width = screenWidth - wpMain.width;
\r
6697 wpConsole.height = wpMain.height;
\r
6698 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6699 wp.length = sizeof(WINDOWPLACEMENT);
\r
6701 wp.showCmd = SW_SHOW;
\r
6702 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6703 wp.rcNormalPosition.left = wpConsole.x;
\r
6704 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6705 wp.rcNormalPosition.top = wpConsole.y;
\r
6706 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6707 SetWindowPlacement(hDlg, &wp);
\r
6710 // Allow hText to highlight URLs and send notifications on them
\r
6711 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6712 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6713 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6714 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6728 if (IsIconic(hDlg)) break;
\r
6729 newSizeX = LOWORD(lParam);
\r
6730 newSizeY = HIWORD(lParam);
\r
6731 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6732 RECT rectText, rectInput;
\r
6734 int newTextHeight, newTextWidth;
\r
6735 GetWindowRect(hText, &rectText);
\r
6736 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6737 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6738 if (newTextHeight < 0) {
\r
6739 newSizeY += -newTextHeight;
\r
6740 newTextHeight = 0;
\r
6742 SetWindowPos(hText, NULL, 0, 0,
\r
6743 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6744 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6745 pt.x = rectInput.left;
\r
6746 pt.y = rectInput.top + newSizeY - sizeY;
\r
6747 ScreenToClient(hDlg, &pt);
\r
6748 SetWindowPos(hInput, NULL,
\r
6749 pt.x, pt.y, /* needs client coords */
\r
6750 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6751 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6757 case WM_GETMINMAXINFO:
\r
6758 /* Prevent resizing window too small */
\r
6759 mmi = (MINMAXINFO *) lParam;
\r
6760 mmi->ptMinTrackSize.x = 100;
\r
6761 mmi->ptMinTrackSize.y = 100;
\r
6764 /* [AS] Snapping */
\r
6765 case WM_ENTERSIZEMOVE:
\r
6766 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6769 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6772 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6774 case WM_EXITSIZEMOVE:
\r
6775 UpdateICSWidth(hText);
\r
6776 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6779 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6787 if (hwndConsole) return;
\r
6788 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6789 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6794 ConsoleOutput(char* data, int length, int forceVisible)
\r
6799 char buf[CO_MAX+1];
\r
6802 static int delayLF = 0;
\r
6803 CHARRANGE savesel, sel;
\r
6805 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6813 while (length--) {
\r
6821 } else if (*p == '\007') {
\r
6822 MyPlaySound(&sounds[(int)SoundBell]);
\r
6829 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6830 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6831 /* Save current selection */
\r
6832 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6833 exlen = GetWindowTextLength(hText);
\r
6834 /* Find out whether current end of text is visible */
\r
6835 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6836 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6837 /* Trim existing text if it's too long */
\r
6838 if (exlen + (q - buf) > CO_MAX) {
\r
6839 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6842 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6843 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6845 savesel.cpMin -= trim;
\r
6846 savesel.cpMax -= trim;
\r
6847 if (exlen < 0) exlen = 0;
\r
6848 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6849 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6851 /* Append the new text */
\r
6852 sel.cpMin = exlen;
\r
6853 sel.cpMax = exlen;
\r
6854 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6855 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6856 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6857 if (forceVisible || exlen == 0 ||
\r
6858 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6859 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6860 /* Scroll to make new end of text visible if old end of text
\r
6861 was visible or new text is an echo of user typein */
\r
6862 sel.cpMin = 9999999;
\r
6863 sel.cpMax = 9999999;
\r
6864 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6865 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6866 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6867 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6869 if (savesel.cpMax == exlen || forceVisible) {
\r
6870 /* Move insert point to new end of text if it was at the old
\r
6871 end of text or if the new text is an echo of user typein */
\r
6872 sel.cpMin = 9999999;
\r
6873 sel.cpMax = 9999999;
\r
6874 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6876 /* Restore previous selection */
\r
6877 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6879 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6886 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6890 COLORREF oldFg, oldBg;
\r
6894 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6896 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6897 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6898 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6901 rect.right = x + squareSize;
\r
6903 rect.bottom = y + squareSize;
\r
6906 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6907 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6908 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6909 &rect, str, strlen(str), NULL);
\r
6911 (void) SetTextColor(hdc, oldFg);
\r
6912 (void) SetBkColor(hdc, oldBg);
\r
6913 (void) SelectObject(hdc, oldFont);
\r
6917 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6918 RECT *rect, char *color, char *flagFell)
\r
6922 COLORREF oldFg, oldBg;
\r
6925 if (appData.clockMode) {
\r
6927 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
6929 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
6936 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6937 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6939 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
6940 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
6942 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6946 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6947 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
6948 rect, str, strlen(str), NULL);
\r
6949 if(logoHeight > 0 && appData.clockMode) {
\r
6951 sprintf(buf, "%s %s", buf+7, flagFell);
\r
6952 r.top = rect->top + logoHeight/2;
\r
6953 r.left = rect->left;
\r
6954 r.right = rect->right;
\r
6955 r.bottom = rect->bottom;
\r
6956 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6957 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
6958 &r, str, strlen(str), NULL);
\r
6960 (void) SetTextColor(hdc, oldFg);
\r
6961 (void) SetBkColor(hdc, oldBg);
\r
6962 (void) SelectObject(hdc, oldFont);
\r
6967 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
6973 if( count <= 0 ) {
\r
6974 if (appData.debugMode) {
\r
6975 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
6978 return ERROR_INVALID_USER_BUFFER;
\r
6981 ResetEvent(ovl->hEvent);
\r
6982 ovl->Offset = ovl->OffsetHigh = 0;
\r
6983 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
6987 err = GetLastError();
\r
6988 if (err == ERROR_IO_PENDING) {
\r
6989 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
6993 err = GetLastError();
\r
7000 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7005 ResetEvent(ovl->hEvent);
\r
7006 ovl->Offset = ovl->OffsetHigh = 0;
\r
7007 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7011 err = GetLastError();
\r
7012 if (err == ERROR_IO_PENDING) {
\r
7013 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7017 err = GetLastError();
\r
7023 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7024 void CheckForInputBufferFull( InputSource * is )
\r
7026 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7027 /* Look for end of line */
\r
7028 char * p = is->buf;
\r
7030 while( p < is->next && *p != '\n' ) {
\r
7034 if( p >= is->next ) {
\r
7035 if (appData.debugMode) {
\r
7036 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7039 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7040 is->count = (DWORD) -1;
\r
7041 is->next = is->buf;
\r
7047 InputThread(LPVOID arg)
\r
7052 is = (InputSource *) arg;
\r
7053 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7054 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7055 while (is->hThread != NULL) {
\r
7056 is->error = DoReadFile(is->hFile, is->next,
\r
7057 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7058 &is->count, &ovl);
\r
7059 if (is->error == NO_ERROR) {
\r
7060 is->next += is->count;
\r
7062 if (is->error == ERROR_BROKEN_PIPE) {
\r
7063 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7066 is->count = (DWORD) -1;
\r
7067 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7072 CheckForInputBufferFull( is );
\r
7074 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7076 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7078 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7081 CloseHandle(ovl.hEvent);
\r
7082 CloseHandle(is->hFile);
\r
7084 if (appData.debugMode) {
\r
7085 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7092 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7094 NonOvlInputThread(LPVOID arg)
\r
7101 is = (InputSource *) arg;
\r
7102 while (is->hThread != NULL) {
\r
7103 is->error = ReadFile(is->hFile, is->next,
\r
7104 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7105 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7106 if (is->error == NO_ERROR) {
\r
7107 /* Change CRLF to LF */
\r
7108 if (is->next > is->buf) {
\r
7110 i = is->count + 1;
\r
7118 if (prev == '\r' && *p == '\n') {
\r
7130 if (is->error == ERROR_BROKEN_PIPE) {
\r
7131 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7134 is->count = (DWORD) -1;
\r
7138 CheckForInputBufferFull( is );
\r
7140 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7142 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7144 if (is->count < 0) break; /* Quit on error */
\r
7146 CloseHandle(is->hFile);
\r
7151 SocketInputThread(LPVOID arg)
\r
7155 is = (InputSource *) arg;
\r
7156 while (is->hThread != NULL) {
\r
7157 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7158 if ((int)is->count == SOCKET_ERROR) {
\r
7159 is->count = (DWORD) -1;
\r
7160 is->error = WSAGetLastError();
\r
7162 is->error = NO_ERROR;
\r
7163 is->next += is->count;
\r
7164 if (is->count == 0 && is->second == is) {
\r
7165 /* End of file on stderr; quit with no message */
\r
7169 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7171 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7173 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7179 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7183 is = (InputSource *) lParam;
\r
7184 if (is->lineByLine) {
\r
7185 /* Feed in lines one by one */
\r
7186 char *p = is->buf;
\r
7188 while (q < is->next) {
\r
7189 if (*q++ == '\n') {
\r
7190 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7195 /* Move any partial line to the start of the buffer */
\r
7197 while (p < is->next) {
\r
7202 if (is->error != NO_ERROR || is->count == 0) {
\r
7203 /* Notify backend of the error. Note: If there was a partial
\r
7204 line at the end, it is not flushed through. */
\r
7205 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7208 /* Feed in the whole chunk of input at once */
\r
7209 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7210 is->next = is->buf;
\r
7214 /*---------------------------------------------------------------------------*\
\r
7216 * Menu enables. Used when setting various modes.
\r
7218 \*---------------------------------------------------------------------------*/
\r
7226 GreyRevert(Boolean grey)
\r
7227 { // [HGM] vari: for retracting variations in local mode
\r
7228 HMENU hmenu = GetMenu(hwndMain);
\r
7229 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7233 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7235 while (enab->item > 0) {
\r
7236 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7241 Enables gnuEnables[] = {
\r
7242 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7243 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7244 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7245 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7246 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7247 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7248 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7249 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7250 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7251 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7252 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7256 Enables icsEnables[] = {
\r
7257 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7258 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7259 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7260 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7261 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7262 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7263 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7264 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7265 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7266 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7267 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7268 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7269 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7270 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7271 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7276 Enables zippyEnables[] = {
\r
7277 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7278 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7279 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7280 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7285 Enables ncpEnables[] = {
\r
7286 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7287 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7288 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7289 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7290 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7291 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7292 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7293 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7294 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7295 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7296 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7297 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7298 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7299 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7300 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7301 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7302 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7303 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7304 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7308 Enables trainingOnEnables[] = {
\r
7309 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7310 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7311 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7312 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7313 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7314 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7315 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7316 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7320 Enables trainingOffEnables[] = {
\r
7321 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7322 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7323 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7324 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7325 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7326 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7327 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7328 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7332 /* These modify either ncpEnables or gnuEnables */
\r
7333 Enables cmailEnables[] = {
\r
7334 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7335 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7336 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7337 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7338 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7339 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7340 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7344 Enables machineThinkingEnables[] = {
\r
7345 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7346 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7347 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7348 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7349 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7350 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7351 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7352 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7353 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7354 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7355 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7356 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7357 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7358 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7359 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7363 Enables userThinkingEnables[] = {
\r
7364 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7365 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7366 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7367 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7368 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7369 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7370 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7371 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7372 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7373 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7374 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7375 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7376 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7377 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7378 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7382 /*---------------------------------------------------------------------------*\
\r
7384 * Front-end interface functions exported by XBoard.
\r
7385 * Functions appear in same order as prototypes in frontend.h.
\r
7387 \*---------------------------------------------------------------------------*/
\r
7391 static UINT prevChecked = 0;
\r
7392 static int prevPausing = 0;
\r
7395 if (pausing != prevPausing) {
\r
7396 prevPausing = pausing;
\r
7397 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7398 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7399 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7402 switch (gameMode) {
\r
7403 case BeginningOfGame:
\r
7404 if (appData.icsActive)
\r
7405 nowChecked = IDM_IcsClient;
\r
7406 else if (appData.noChessProgram)
\r
7407 nowChecked = IDM_EditGame;
\r
7409 nowChecked = IDM_MachineBlack;
\r
7411 case MachinePlaysBlack:
\r
7412 nowChecked = IDM_MachineBlack;
\r
7414 case MachinePlaysWhite:
\r
7415 nowChecked = IDM_MachineWhite;
\r
7417 case TwoMachinesPlay:
\r
7418 nowChecked = IDM_TwoMachines;
\r
7421 nowChecked = IDM_AnalysisMode;
\r
7424 nowChecked = IDM_AnalyzeFile;
\r
7427 nowChecked = IDM_EditGame;
\r
7429 case PlayFromGameFile:
\r
7430 nowChecked = IDM_LoadGame;
\r
7432 case EditPosition:
\r
7433 nowChecked = IDM_EditPosition;
\r
7436 nowChecked = IDM_Training;
\r
7438 case IcsPlayingWhite:
\r
7439 case IcsPlayingBlack:
\r
7440 case IcsObserving:
\r
7442 nowChecked = IDM_IcsClient;
\r
7449 if (prevChecked != 0)
\r
7450 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7451 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7452 if (nowChecked != 0)
\r
7453 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7454 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7456 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7457 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7458 MF_BYCOMMAND|MF_ENABLED);
\r
7460 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7461 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7464 prevChecked = nowChecked;
\r
7466 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7467 if (appData.icsActive) {
\r
7468 if (appData.icsEngineAnalyze) {
\r
7469 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7470 MF_BYCOMMAND|MF_CHECKED);
\r
7472 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7473 MF_BYCOMMAND|MF_UNCHECKED);
\r
7481 HMENU hmenu = GetMenu(hwndMain);
\r
7482 SetMenuEnables(hmenu, icsEnables);
\r
7483 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7484 MF_BYPOSITION|MF_ENABLED);
\r
7486 if (appData.zippyPlay) {
\r
7487 SetMenuEnables(hmenu, zippyEnables);
\r
7488 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7489 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7490 MF_BYCOMMAND|MF_ENABLED);
\r
7498 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7504 HMENU hmenu = GetMenu(hwndMain);
\r
7505 SetMenuEnables(hmenu, ncpEnables);
\r
7506 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7507 MF_BYPOSITION|MF_GRAYED);
\r
7508 DrawMenuBar(hwndMain);
\r
7514 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7518 SetTrainingModeOn()
\r
7521 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7522 for (i = 0; i < N_BUTTONS; i++) {
\r
7523 if (buttonDesc[i].hwnd != NULL)
\r
7524 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7529 VOID SetTrainingModeOff()
\r
7532 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7533 for (i = 0; i < N_BUTTONS; i++) {
\r
7534 if (buttonDesc[i].hwnd != NULL)
\r
7535 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7541 SetUserThinkingEnables()
\r
7543 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7547 SetMachineThinkingEnables()
\r
7549 HMENU hMenu = GetMenu(hwndMain);
\r
7550 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7552 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7554 if (gameMode == MachinePlaysBlack) {
\r
7555 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7556 } else if (gameMode == MachinePlaysWhite) {
\r
7557 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7558 } else if (gameMode == TwoMachinesPlay) {
\r
7559 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7565 DisplayTitle(char *str)
\r
7567 char title[MSG_SIZ], *host;
\r
7568 if (str[0] != NULLCHAR) {
\r
7569 strcpy(title, str);
\r
7570 } else if (appData.icsActive) {
\r
7571 if (appData.icsCommPort[0] != NULLCHAR)
\r
7574 host = appData.icsHost;
\r
7575 sprintf(title, "%s: %s", szTitle, host);
\r
7576 } else if (appData.noChessProgram) {
\r
7577 strcpy(title, szTitle);
\r
7579 strcpy(title, szTitle);
\r
7580 strcat(title, ": ");
\r
7581 strcat(title, first.tidy);
\r
7583 SetWindowText(hwndMain, title);
\r
7588 DisplayMessage(char *str1, char *str2)
\r
7592 int remain = MESSAGE_TEXT_MAX - 1;
\r
7595 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7596 messageText[0] = NULLCHAR;
\r
7598 len = strlen(str1);
\r
7599 if (len > remain) len = remain;
\r
7600 strncpy(messageText, str1, len);
\r
7601 messageText[len] = NULLCHAR;
\r
7604 if (*str2 && remain >= 2) {
\r
7606 strcat(messageText, " ");
\r
7609 len = strlen(str2);
\r
7610 if (len > remain) len = remain;
\r
7611 strncat(messageText, str2, len);
\r
7613 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7615 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7619 hdc = GetDC(hwndMain);
\r
7620 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7621 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7622 &messageRect, messageText, strlen(messageText), NULL);
\r
7623 (void) SelectObject(hdc, oldFont);
\r
7624 (void) ReleaseDC(hwndMain, hdc);
\r
7628 DisplayError(char *str, int error)
\r
7630 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7636 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7637 NULL, error, LANG_NEUTRAL,
\r
7638 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7640 sprintf(buf, "%s:\n%s", str, buf2);
\r
7642 ErrorMap *em = errmap;
\r
7643 while (em->err != 0 && em->err != error) em++;
\r
7644 if (em->err != 0) {
\r
7645 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7647 sprintf(buf, "%s:\nError code %d", str, error);
\r
7652 ErrorPopUp("Error", buf);
\r
7657 DisplayMoveError(char *str)
\r
7659 fromX = fromY = -1;
\r
7660 ClearHighlights();
\r
7661 DrawPosition(FALSE, NULL);
\r
7662 if (appData.popupMoveErrors) {
\r
7663 ErrorPopUp("Error", str);
\r
7665 DisplayMessage(str, "");
\r
7666 moveErrorMessageUp = TRUE;
\r
7671 DisplayFatalError(char *str, int error, int exitStatus)
\r
7673 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7675 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7678 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7679 NULL, error, LANG_NEUTRAL,
\r
7680 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7682 sprintf(buf, "%s:\n%s", str, buf2);
\r
7684 ErrorMap *em = errmap;
\r
7685 while (em->err != 0 && em->err != error) em++;
\r
7686 if (em->err != 0) {
\r
7687 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7689 sprintf(buf, "%s:\nError code %d", str, error);
\r
7694 if (appData.debugMode) {
\r
7695 fprintf(debugFP, "%s: %s\n", label, str);
\r
7697 if (appData.popupExitMessage) {
\r
7698 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7699 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7701 ExitEvent(exitStatus);
\r
7706 DisplayInformation(char *str)
\r
7708 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7713 DisplayNote(char *str)
\r
7715 ErrorPopUp("Note", str);
\r
7720 char *title, *question, *replyPrefix;
\r
7725 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7727 static QuestionParams *qp;
\r
7728 char reply[MSG_SIZ];
\r
7731 switch (message) {
\r
7732 case WM_INITDIALOG:
\r
7733 qp = (QuestionParams *) lParam;
\r
7734 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7735 SetWindowText(hDlg, qp->title);
\r
7736 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7737 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7741 switch (LOWORD(wParam)) {
\r
7743 strcpy(reply, qp->replyPrefix);
\r
7744 if (*reply) strcat(reply, " ");
\r
7745 len = strlen(reply);
\r
7746 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7747 strcat(reply, "\n");
\r
7748 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7749 EndDialog(hDlg, TRUE);
\r
7750 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7753 EndDialog(hDlg, FALSE);
\r
7764 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7766 QuestionParams qp;
\r
7770 qp.question = question;
\r
7771 qp.replyPrefix = replyPrefix;
\r
7773 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7774 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7775 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7776 FreeProcInstance(lpProc);
\r
7779 /* [AS] Pick FRC position */
\r
7780 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7782 static int * lpIndexFRC;
\r
7788 case WM_INITDIALOG:
\r
7789 lpIndexFRC = (int *) lParam;
\r
7791 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7793 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7794 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7795 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7796 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7801 switch( LOWORD(wParam) ) {
\r
7803 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7804 EndDialog( hDlg, 0 );
\r
7805 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7808 EndDialog( hDlg, 1 );
\r
7810 case IDC_NFG_Edit:
\r
7811 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7812 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7814 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7817 case IDC_NFG_Random:
\r
7818 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7819 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7832 int index = appData.defaultFrcPosition;
\r
7833 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7835 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7837 if( result == 0 ) {
\r
7838 appData.defaultFrcPosition = index;
\r
7844 /* [AS] Game list options. Refactored by HGM */
\r
7846 HWND gameListOptionsDialog;
\r
7848 // low-level front-end: clear text edit / list widget
\r
7852 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7855 // low-level front-end: clear text edit / list widget
\r
7857 GLT_DeSelectList()
\r
7859 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7862 // low-level front-end: append line to text edit / list widget
\r
7864 GLT_AddToList( char *name )
\r
7867 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
7871 // low-level front-end: get line from text edit / list widget
\r
7873 GLT_GetFromList( int index, char *name )
\r
7876 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
7882 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7884 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7885 int idx2 = idx1 + delta;
\r
7886 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7888 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7891 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7892 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7893 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7894 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7898 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7902 case WM_INITDIALOG:
\r
7903 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
7905 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7907 /* Initialize list */
\r
7908 GLT_TagsToList( lpUserGLT );
\r
7910 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
7915 switch( LOWORD(wParam) ) {
\r
7918 EndDialog( hDlg, 0 );
\r
7921 EndDialog( hDlg, 1 );
\r
7924 case IDC_GLT_Default:
\r
7925 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
7928 case IDC_GLT_Restore:
\r
7929 GLT_TagsToList( appData.gameListTags );
\r
7933 GLT_MoveSelection( hDlg, -1 );
\r
7936 case IDC_GLT_Down:
\r
7937 GLT_MoveSelection( hDlg, +1 );
\r
7947 int GameListOptions()
\r
7950 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
7952 strcpy( lpUserGLT, appData.gameListTags );
\r
7954 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
7956 if( result == 0 ) {
\r
7957 /* [AS] Memory leak here! */
\r
7958 appData.gameListTags = strdup( lpUserGLT );
\r
7965 DisplayIcsInteractionTitle(char *str)
\r
7967 char consoleTitle[MSG_SIZ];
\r
7969 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
7970 SetWindowText(hwndConsole, consoleTitle);
\r
7974 DrawPosition(int fullRedraw, Board board)
\r
7976 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
7979 void NotifyFrontendLogin()
\r
7982 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7988 fromX = fromY = -1;
\r
7989 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
7990 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
7991 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
7992 dragInfo.lastpos = dragInfo.pos;
\r
7993 dragInfo.start.x = dragInfo.start.y = -1;
\r
7994 dragInfo.from = dragInfo.start;
\r
7996 DrawPosition(TRUE, NULL);
\r
8002 CommentPopUp(char *title, char *str)
\r
8004 HWND hwnd = GetActiveWindow();
\r
8005 EitherCommentPopUp(0, title, str, FALSE);
\r
8007 SetActiveWindow(hwnd);
\r
8011 CommentPopDown(void)
\r
8013 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8014 if (commentDialog) {
\r
8015 ShowWindow(commentDialog, SW_HIDE);
\r
8017 commentUp = FALSE;
\r
8021 EditCommentPopUp(int index, char *title, char *str)
\r
8023 EitherCommentPopUp(index, title, str, TRUE);
\r
8030 MyPlaySound(&sounds[(int)SoundMove]);
\r
8033 VOID PlayIcsWinSound()
\r
8035 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8038 VOID PlayIcsLossSound()
\r
8040 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8043 VOID PlayIcsDrawSound()
\r
8045 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8048 VOID PlayIcsUnfinishedSound()
\r
8050 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8056 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8064 consoleEcho = TRUE;
\r
8065 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8066 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8067 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8076 consoleEcho = FALSE;
\r
8077 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8078 /* This works OK: set text and background both to the same color */
\r
8080 cf.crTextColor = COLOR_ECHOOFF;
\r
8081 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8082 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8085 /* No Raw()...? */
\r
8087 void Colorize(ColorClass cc, int continuation)
\r
8089 currentColorClass = cc;
\r
8090 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8091 consoleCF.crTextColor = textAttribs[cc].color;
\r
8092 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8093 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8099 static char buf[MSG_SIZ];
\r
8100 DWORD bufsiz = MSG_SIZ;
\r
8102 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8103 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8105 if (!GetUserName(buf, &bufsiz)) {
\r
8106 /*DisplayError("Error getting user name", GetLastError());*/
\r
8107 strcpy(buf, "User");
\r
8115 static char buf[MSG_SIZ];
\r
8116 DWORD bufsiz = MSG_SIZ;
\r
8118 if (!GetComputerName(buf, &bufsiz)) {
\r
8119 /*DisplayError("Error getting host name", GetLastError());*/
\r
8120 strcpy(buf, "Unknown");
\r
8127 ClockTimerRunning()
\r
8129 return clockTimerEvent != 0;
\r
8135 if (clockTimerEvent == 0) return FALSE;
\r
8136 KillTimer(hwndMain, clockTimerEvent);
\r
8137 clockTimerEvent = 0;
\r
8142 StartClockTimer(long millisec)
\r
8144 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8145 (UINT) millisec, NULL);
\r
8149 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8152 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8154 if(appData.noGUI) return;
\r
8155 hdc = GetDC(hwndMain);
\r
8156 if (!IsIconic(hwndMain)) {
\r
8157 DisplayAClock(hdc, timeRemaining, highlight,
\r
8158 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8160 if (highlight && iconCurrent == iconBlack) {
\r
8161 iconCurrent = iconWhite;
\r
8162 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8163 if (IsIconic(hwndMain)) {
\r
8164 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8167 (void) ReleaseDC(hwndMain, hdc);
\r
8169 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8173 DisplayBlackClock(long timeRemaining, int highlight)
\r
8176 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8178 if(appData.noGUI) return;
\r
8179 hdc = GetDC(hwndMain);
\r
8180 if (!IsIconic(hwndMain)) {
\r
8181 DisplayAClock(hdc, timeRemaining, highlight,
\r
8182 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8184 if (highlight && iconCurrent == iconWhite) {
\r
8185 iconCurrent = iconBlack;
\r
8186 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8187 if (IsIconic(hwndMain)) {
\r
8188 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8191 (void) ReleaseDC(hwndMain, hdc);
\r
8193 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8198 LoadGameTimerRunning()
\r
8200 return loadGameTimerEvent != 0;
\r
8204 StopLoadGameTimer()
\r
8206 if (loadGameTimerEvent == 0) return FALSE;
\r
8207 KillTimer(hwndMain, loadGameTimerEvent);
\r
8208 loadGameTimerEvent = 0;
\r
8213 StartLoadGameTimer(long millisec)
\r
8215 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8216 (UINT) millisec, NULL);
\r
8224 char fileTitle[MSG_SIZ];
\r
8226 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8227 f = OpenFileDialog(hwndMain, "a", defName,
\r
8228 appData.oldSaveStyle ? "gam" : "pgn",
\r
8230 "Save Game to File", NULL, fileTitle, NULL);
\r
8232 SaveGame(f, 0, "");
\r
8239 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8241 if (delayedTimerEvent != 0) {
\r
8242 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8243 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8245 KillTimer(hwndMain, delayedTimerEvent);
\r
8246 delayedTimerEvent = 0;
\r
8247 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8248 delayedTimerCallback();
\r
8250 delayedTimerCallback = cb;
\r
8251 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8252 (UINT) millisec, NULL);
\r
8255 DelayedEventCallback
\r
8258 if (delayedTimerEvent) {
\r
8259 return delayedTimerCallback;
\r
8266 CancelDelayedEvent()
\r
8268 if (delayedTimerEvent) {
\r
8269 KillTimer(hwndMain, delayedTimerEvent);
\r
8270 delayedTimerEvent = 0;
\r
8274 DWORD GetWin32Priority(int nice)
\r
8275 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8277 REALTIME_PRIORITY_CLASS 0x00000100
\r
8278 HIGH_PRIORITY_CLASS 0x00000080
\r
8279 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8280 NORMAL_PRIORITY_CLASS 0x00000020
\r
8281 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8282 IDLE_PRIORITY_CLASS 0x00000040
\r
8284 if (nice < -15) return 0x00000080;
\r
8285 if (nice < 0) return 0x00008000;
\r
8286 if (nice == 0) return 0x00000020;
\r
8287 if (nice < 15) return 0x00004000;
\r
8288 return 0x00000040;
\r
8291 /* Start a child process running the given program.
\r
8292 The process's standard output can be read from "from", and its
\r
8293 standard input can be written to "to".
\r
8294 Exit with fatal error if anything goes wrong.
\r
8295 Returns an opaque pointer that can be used to destroy the process
\r
8299 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8301 #define BUFSIZE 4096
\r
8303 HANDLE hChildStdinRd, hChildStdinWr,
\r
8304 hChildStdoutRd, hChildStdoutWr;
\r
8305 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8306 SECURITY_ATTRIBUTES saAttr;
\r
8308 PROCESS_INFORMATION piProcInfo;
\r
8309 STARTUPINFO siStartInfo;
\r
8311 char buf[MSG_SIZ];
\r
8314 if (appData.debugMode) {
\r
8315 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8320 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8321 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8322 saAttr.bInheritHandle = TRUE;
\r
8323 saAttr.lpSecurityDescriptor = NULL;
\r
8326 * The steps for redirecting child's STDOUT:
\r
8327 * 1. Create anonymous pipe to be STDOUT for child.
\r
8328 * 2. Create a noninheritable duplicate of read handle,
\r
8329 * and close the inheritable read handle.
\r
8332 /* Create a pipe for the child's STDOUT. */
\r
8333 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8334 return GetLastError();
\r
8337 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8338 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8339 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8340 FALSE, /* not inherited */
\r
8341 DUPLICATE_SAME_ACCESS);
\r
8343 return GetLastError();
\r
8345 CloseHandle(hChildStdoutRd);
\r
8348 * The steps for redirecting child's STDIN:
\r
8349 * 1. Create anonymous pipe to be STDIN for child.
\r
8350 * 2. Create a noninheritable duplicate of write handle,
\r
8351 * and close the inheritable write handle.
\r
8354 /* Create a pipe for the child's STDIN. */
\r
8355 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8356 return GetLastError();
\r
8359 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8360 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8361 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8362 FALSE, /* not inherited */
\r
8363 DUPLICATE_SAME_ACCESS);
\r
8365 return GetLastError();
\r
8367 CloseHandle(hChildStdinWr);
\r
8369 /* Arrange to (1) look in dir for the child .exe file, and
\r
8370 * (2) have dir be the child's working directory. Interpret
\r
8371 * dir relative to the directory WinBoard loaded from. */
\r
8372 GetCurrentDirectory(MSG_SIZ, buf);
\r
8373 SetCurrentDirectory(installDir);
\r
8374 SetCurrentDirectory(dir);
\r
8376 /* Now create the child process. */
\r
8378 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8379 siStartInfo.lpReserved = NULL;
\r
8380 siStartInfo.lpDesktop = NULL;
\r
8381 siStartInfo.lpTitle = NULL;
\r
8382 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8383 siStartInfo.cbReserved2 = 0;
\r
8384 siStartInfo.lpReserved2 = NULL;
\r
8385 siStartInfo.hStdInput = hChildStdinRd;
\r
8386 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8387 siStartInfo.hStdError = hChildStdoutWr;
\r
8389 fSuccess = CreateProcess(NULL,
\r
8390 cmdLine, /* command line */
\r
8391 NULL, /* process security attributes */
\r
8392 NULL, /* primary thread security attrs */
\r
8393 TRUE, /* handles are inherited */
\r
8394 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8395 NULL, /* use parent's environment */
\r
8397 &siStartInfo, /* STARTUPINFO pointer */
\r
8398 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8400 err = GetLastError();
\r
8401 SetCurrentDirectory(buf); /* return to prev directory */
\r
8406 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8407 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8408 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8411 /* Close the handles we don't need in the parent */
\r
8412 CloseHandle(piProcInfo.hThread);
\r
8413 CloseHandle(hChildStdinRd);
\r
8414 CloseHandle(hChildStdoutWr);
\r
8416 /* Prepare return value */
\r
8417 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8418 cp->kind = CPReal;
\r
8419 cp->hProcess = piProcInfo.hProcess;
\r
8420 cp->pid = piProcInfo.dwProcessId;
\r
8421 cp->hFrom = hChildStdoutRdDup;
\r
8422 cp->hTo = hChildStdinWrDup;
\r
8424 *pr = (void *) cp;
\r
8426 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8427 2000 where engines sometimes don't see the initial command(s)
\r
8428 from WinBoard and hang. I don't understand how that can happen,
\r
8429 but the Sleep is harmless, so I've put it in. Others have also
\r
8430 reported what may be the same problem, so hopefully this will fix
\r
8431 it for them too. */
\r
8439 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8441 ChildProc *cp; int result;
\r
8443 cp = (ChildProc *) pr;
\r
8444 if (cp == NULL) return;
\r
8446 switch (cp->kind) {
\r
8448 /* TerminateProcess is considered harmful, so... */
\r
8449 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8450 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8451 /* The following doesn't work because the chess program
\r
8452 doesn't "have the same console" as WinBoard. Maybe
\r
8453 we could arrange for this even though neither WinBoard
\r
8454 nor the chess program uses a console for stdio? */
\r
8455 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8457 /* [AS] Special termination modes for misbehaving programs... */
\r
8458 if( signal == 9 ) {
\r
8459 result = TerminateProcess( cp->hProcess, 0 );
\r
8461 if ( appData.debugMode) {
\r
8462 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8465 else if( signal == 10 ) {
\r
8466 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8468 if( dw != WAIT_OBJECT_0 ) {
\r
8469 result = TerminateProcess( cp->hProcess, 0 );
\r
8471 if ( appData.debugMode) {
\r
8472 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8478 CloseHandle(cp->hProcess);
\r
8482 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8486 closesocket(cp->sock);
\r
8491 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8492 closesocket(cp->sock);
\r
8493 closesocket(cp->sock2);
\r
8501 InterruptChildProcess(ProcRef pr)
\r
8505 cp = (ChildProc *) pr;
\r
8506 if (cp == NULL) return;
\r
8507 switch (cp->kind) {
\r
8509 /* The following doesn't work because the chess program
\r
8510 doesn't "have the same console" as WinBoard. Maybe
\r
8511 we could arrange for this even though neither WinBoard
\r
8512 nor the chess program uses a console for stdio */
\r
8513 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8518 /* Can't interrupt */
\r
8522 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8529 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8531 char cmdLine[MSG_SIZ];
\r
8533 if (port[0] == NULLCHAR) {
\r
8534 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8536 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8538 return StartChildProcess(cmdLine, "", pr);
\r
8542 /* Code to open TCP sockets */
\r
8545 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8550 struct sockaddr_in sa, mysa;
\r
8551 struct hostent FAR *hp;
\r
8552 unsigned short uport;
\r
8553 WORD wVersionRequested;
\r
8556 /* Initialize socket DLL */
\r
8557 wVersionRequested = MAKEWORD(1, 1);
\r
8558 err = WSAStartup(wVersionRequested, &wsaData);
\r
8559 if (err != 0) return err;
\r
8562 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8563 err = WSAGetLastError();
\r
8568 /* Bind local address using (mostly) don't-care values.
\r
8570 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8571 mysa.sin_family = AF_INET;
\r
8572 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8573 uport = (unsigned short) 0;
\r
8574 mysa.sin_port = htons(uport);
\r
8575 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8576 == SOCKET_ERROR) {
\r
8577 err = WSAGetLastError();
\r
8582 /* Resolve remote host name */
\r
8583 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8584 if (!(hp = gethostbyname(host))) {
\r
8585 unsigned int b0, b1, b2, b3;
\r
8587 err = WSAGetLastError();
\r
8589 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8590 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8591 hp->h_addrtype = AF_INET;
\r
8593 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8594 hp->h_addr_list[0] = (char *) malloc(4);
\r
8595 hp->h_addr_list[0][0] = (char) b0;
\r
8596 hp->h_addr_list[0][1] = (char) b1;
\r
8597 hp->h_addr_list[0][2] = (char) b2;
\r
8598 hp->h_addr_list[0][3] = (char) b3;
\r
8604 sa.sin_family = hp->h_addrtype;
\r
8605 uport = (unsigned short) atoi(port);
\r
8606 sa.sin_port = htons(uport);
\r
8607 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8609 /* Make connection */
\r
8610 if (connect(s, (struct sockaddr *) &sa,
\r
8611 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8612 err = WSAGetLastError();
\r
8617 /* Prepare return value */
\r
8618 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8619 cp->kind = CPSock;
\r
8621 *pr = (ProcRef *) cp;
\r
8627 OpenCommPort(char *name, ProcRef *pr)
\r
8632 char fullname[MSG_SIZ];
\r
8634 if (*name != '\\')
\r
8635 sprintf(fullname, "\\\\.\\%s", name);
\r
8637 strcpy(fullname, name);
\r
8639 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8640 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8641 if (h == (HANDLE) -1) {
\r
8642 return GetLastError();
\r
8646 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8648 /* Accumulate characters until a 100ms pause, then parse */
\r
8649 ct.ReadIntervalTimeout = 100;
\r
8650 ct.ReadTotalTimeoutMultiplier = 0;
\r
8651 ct.ReadTotalTimeoutConstant = 0;
\r
8652 ct.WriteTotalTimeoutMultiplier = 0;
\r
8653 ct.WriteTotalTimeoutConstant = 0;
\r
8654 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8656 /* Prepare return value */
\r
8657 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8658 cp->kind = CPComm;
\r
8661 *pr = (ProcRef *) cp;
\r
8667 OpenLoopback(ProcRef *pr)
\r
8669 DisplayFatalError("Not implemented", 0, 1);
\r
8675 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8680 struct sockaddr_in sa, mysa;
\r
8681 struct hostent FAR *hp;
\r
8682 unsigned short uport;
\r
8683 WORD wVersionRequested;
\r
8686 char stderrPortStr[MSG_SIZ];
\r
8688 /* Initialize socket DLL */
\r
8689 wVersionRequested = MAKEWORD(1, 1);
\r
8690 err = WSAStartup(wVersionRequested, &wsaData);
\r
8691 if (err != 0) return err;
\r
8693 /* Resolve remote host name */
\r
8694 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8695 if (!(hp = gethostbyname(host))) {
\r
8696 unsigned int b0, b1, b2, b3;
\r
8698 err = WSAGetLastError();
\r
8700 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8701 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8702 hp->h_addrtype = AF_INET;
\r
8704 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8705 hp->h_addr_list[0] = (char *) malloc(4);
\r
8706 hp->h_addr_list[0][0] = (char) b0;
\r
8707 hp->h_addr_list[0][1] = (char) b1;
\r
8708 hp->h_addr_list[0][2] = (char) b2;
\r
8709 hp->h_addr_list[0][3] = (char) b3;
\r
8715 sa.sin_family = hp->h_addrtype;
\r
8716 uport = (unsigned short) 514;
\r
8717 sa.sin_port = htons(uport);
\r
8718 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8720 /* Bind local socket to unused "privileged" port address
\r
8722 s = INVALID_SOCKET;
\r
8723 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8724 mysa.sin_family = AF_INET;
\r
8725 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8726 for (fromPort = 1023;; fromPort--) {
\r
8727 if (fromPort < 0) {
\r
8729 return WSAEADDRINUSE;
\r
8731 if (s == INVALID_SOCKET) {
\r
8732 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8733 err = WSAGetLastError();
\r
8738 uport = (unsigned short) fromPort;
\r
8739 mysa.sin_port = htons(uport);
\r
8740 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8741 == SOCKET_ERROR) {
\r
8742 err = WSAGetLastError();
\r
8743 if (err == WSAEADDRINUSE) continue;
\r
8747 if (connect(s, (struct sockaddr *) &sa,
\r
8748 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8749 err = WSAGetLastError();
\r
8750 if (err == WSAEADDRINUSE) {
\r
8761 /* Bind stderr local socket to unused "privileged" port address
\r
8763 s2 = INVALID_SOCKET;
\r
8764 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8765 mysa.sin_family = AF_INET;
\r
8766 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8767 for (fromPort = 1023;; fromPort--) {
\r
8768 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8769 if (fromPort < 0) {
\r
8770 (void) closesocket(s);
\r
8772 return WSAEADDRINUSE;
\r
8774 if (s2 == INVALID_SOCKET) {
\r
8775 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8776 err = WSAGetLastError();
\r
8782 uport = (unsigned short) fromPort;
\r
8783 mysa.sin_port = htons(uport);
\r
8784 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8785 == SOCKET_ERROR) {
\r
8786 err = WSAGetLastError();
\r
8787 if (err == WSAEADDRINUSE) continue;
\r
8788 (void) closesocket(s);
\r
8792 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8793 err = WSAGetLastError();
\r
8794 if (err == WSAEADDRINUSE) {
\r
8796 s2 = INVALID_SOCKET;
\r
8799 (void) closesocket(s);
\r
8800 (void) closesocket(s2);
\r
8806 prevStderrPort = fromPort; // remember port used
\r
8807 sprintf(stderrPortStr, "%d", fromPort);
\r
8809 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8810 err = WSAGetLastError();
\r
8811 (void) closesocket(s);
\r
8812 (void) closesocket(s2);
\r
8817 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8818 err = WSAGetLastError();
\r
8819 (void) closesocket(s);
\r
8820 (void) closesocket(s2);
\r
8824 if (*user == NULLCHAR) user = UserName();
\r
8825 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8826 err = WSAGetLastError();
\r
8827 (void) closesocket(s);
\r
8828 (void) closesocket(s2);
\r
8832 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8833 err = WSAGetLastError();
\r
8834 (void) closesocket(s);
\r
8835 (void) closesocket(s2);
\r
8840 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8841 err = WSAGetLastError();
\r
8842 (void) closesocket(s);
\r
8843 (void) closesocket(s2);
\r
8847 (void) closesocket(s2); /* Stop listening */
\r
8849 /* Prepare return value */
\r
8850 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8851 cp->kind = CPRcmd;
\r
8854 *pr = (ProcRef *) cp;
\r
8861 AddInputSource(ProcRef pr, int lineByLine,
\r
8862 InputCallback func, VOIDSTAR closure)
\r
8864 InputSource *is, *is2 = NULL;
\r
8865 ChildProc *cp = (ChildProc *) pr;
\r
8867 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8868 is->lineByLine = lineByLine;
\r
8870 is->closure = closure;
\r
8871 is->second = NULL;
\r
8872 is->next = is->buf;
\r
8873 if (pr == NoProc) {
\r
8874 is->kind = CPReal;
\r
8875 consoleInputSource = is;
\r
8877 is->kind = cp->kind;
\r
8879 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8880 we create all threads suspended so that the is->hThread variable can be
\r
8881 safely assigned, then let the threads start with ResumeThread.
\r
8883 switch (cp->kind) {
\r
8885 is->hFile = cp->hFrom;
\r
8886 cp->hFrom = NULL; /* now owned by InputThread */
\r
8888 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8889 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8893 is->hFile = cp->hFrom;
\r
8894 cp->hFrom = NULL; /* now owned by InputThread */
\r
8896 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
8897 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8901 is->sock = cp->sock;
\r
8903 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8904 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8908 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
8910 is->sock = cp->sock;
\r
8912 is2->sock = cp->sock2;
\r
8913 is2->second = is2;
\r
8915 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8916 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8918 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8919 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
8923 if( is->hThread != NULL ) {
\r
8924 ResumeThread( is->hThread );
\r
8927 if( is2 != NULL && is2->hThread != NULL ) {
\r
8928 ResumeThread( is2->hThread );
\r
8932 return (InputSourceRef) is;
\r
8936 RemoveInputSource(InputSourceRef isr)
\r
8940 is = (InputSource *) isr;
\r
8941 is->hThread = NULL; /* tell thread to stop */
\r
8942 CloseHandle(is->hThread);
\r
8943 if (is->second != NULL) {
\r
8944 is->second->hThread = NULL;
\r
8945 CloseHandle(is->second->hThread);
\r
8949 int no_wrap(char *message, int count)
\r
8951 ConsoleOutput(message, count, FALSE);
\r
8956 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
8959 int outCount = SOCKET_ERROR;
\r
8960 ChildProc *cp = (ChildProc *) pr;
\r
8961 static OVERLAPPED ovl;
\r
8962 static int line = 0;
\r
8966 if (appData.noJoin || !appData.useInternalWrap)
\r
8967 return no_wrap(message, count);
\r
8970 int width = get_term_width();
\r
8971 int len = wrap(NULL, message, count, width, &line);
\r
8972 char *msg = malloc(len);
\r
8976 return no_wrap(message, count);
\r
8979 dbgchk = wrap(msg, message, count, width, &line);
\r
8980 if (dbgchk != len && appData.debugMode)
\r
8981 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
8982 ConsoleOutput(msg, len, FALSE);
\r
8989 if (ovl.hEvent == NULL) {
\r
8990 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
8992 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
8994 switch (cp->kind) {
\r
8997 outCount = send(cp->sock, message, count, 0);
\r
8998 if (outCount == SOCKET_ERROR) {
\r
8999 *outError = WSAGetLastError();
\r
9001 *outError = NO_ERROR;
\r
9006 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9007 &dOutCount, NULL)) {
\r
9008 *outError = NO_ERROR;
\r
9009 outCount = (int) dOutCount;
\r
9011 *outError = GetLastError();
\r
9016 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9017 &dOutCount, &ovl);
\r
9018 if (*outError == NO_ERROR) {
\r
9019 outCount = (int) dOutCount;
\r
9027 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9030 /* Ignore delay, not implemented for WinBoard */
\r
9031 return OutputToProcess(pr, message, count, outError);
\r
9036 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9037 char *buf, int count, int error)
\r
9039 DisplayFatalError("Not implemented", 0, 1);
\r
9042 /* see wgamelist.c for Game List functions */
\r
9043 /* see wedittags.c for Edit Tags functions */
\r
9050 char buf[MSG_SIZ];
\r
9053 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9054 f = fopen(buf, "r");
\r
9056 ProcessICSInitScript(f);
\r
9064 StartAnalysisClock()
\r
9066 if (analysisTimerEvent) return;
\r
9067 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9068 (UINT) 2000, NULL);
\r
9072 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9074 highlightInfo.sq[0].x = fromX;
\r
9075 highlightInfo.sq[0].y = fromY;
\r
9076 highlightInfo.sq[1].x = toX;
\r
9077 highlightInfo.sq[1].y = toY;
\r
9083 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9084 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9088 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9090 premoveHighlightInfo.sq[0].x = fromX;
\r
9091 premoveHighlightInfo.sq[0].y = fromY;
\r
9092 premoveHighlightInfo.sq[1].x = toX;
\r
9093 premoveHighlightInfo.sq[1].y = toY;
\r
9097 ClearPremoveHighlights()
\r
9099 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9100 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9104 ShutDownFrontEnd()
\r
9106 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9107 DeleteClipboardTempFiles();
\r
9113 if (IsIconic(hwndMain))
\r
9114 ShowWindow(hwndMain, SW_RESTORE);
\r
9116 SetActiveWindow(hwndMain);
\r
9120 * Prototypes for animation support routines
\r
9122 static void ScreenSquare(int column, int row, POINT * pt);
\r
9123 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9124 POINT frames[], int * nFrames);
\r
9128 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9129 { // [HGM] atomic: animate blast wave
\r
9131 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9132 explodeInfo.fromX = fromX;
\r
9133 explodeInfo.fromY = fromY;
\r
9134 explodeInfo.toX = toX;
\r
9135 explodeInfo.toY = toY;
\r
9136 for(i=1; i<nFrames; i++) {
\r
9137 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9138 DrawPosition(FALSE, NULL);
\r
9139 Sleep(appData.animSpeed);
\r
9141 explodeInfo.radius = 0;
\r
9142 DrawPosition(TRUE, NULL);
\r
9148 AnimateMove(board, fromX, fromY, toX, toY)
\r
9155 ChessSquare piece;
\r
9156 POINT start, finish, mid;
\r
9157 POINT frames[kFactor * 2 + 1];
\r
9160 if (!appData.animate) return;
\r
9161 if (doingSizing) return;
\r
9162 if (fromY < 0 || fromX < 0) return;
\r
9163 piece = board[fromY][fromX];
\r
9164 if (piece >= EmptySquare) return;
\r
9166 ScreenSquare(fromX, fromY, &start);
\r
9167 ScreenSquare(toX, toY, &finish);
\r
9169 /* All pieces except knights move in straight line */
\r
9170 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9171 mid.x = start.x + (finish.x - start.x) / 2;
\r
9172 mid.y = start.y + (finish.y - start.y) / 2;
\r
9174 /* Knight: make diagonal movement then straight */
\r
9175 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9176 mid.x = start.x + (finish.x - start.x) / 2;
\r
9180 mid.y = start.y + (finish.y - start.y) / 2;
\r
9184 /* Don't use as many frames for very short moves */
\r
9185 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9186 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9188 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9190 animInfo.from.x = fromX;
\r
9191 animInfo.from.y = fromY;
\r
9192 animInfo.to.x = toX;
\r
9193 animInfo.to.y = toY;
\r
9194 animInfo.lastpos = start;
\r
9195 animInfo.piece = piece;
\r
9196 for (n = 0; n < nFrames; n++) {
\r
9197 animInfo.pos = frames[n];
\r
9198 DrawPosition(FALSE, NULL);
\r
9199 animInfo.lastpos = animInfo.pos;
\r
9200 Sleep(appData.animSpeed);
\r
9202 animInfo.pos = finish;
\r
9203 DrawPosition(FALSE, NULL);
\r
9204 animInfo.piece = EmptySquare;
\r
9205 if(gameInfo.variant == VariantAtomic &&
\r
9206 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9207 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9210 /* Convert board position to corner of screen rect and color */
\r
9213 ScreenSquare(column, row, pt)
\r
9214 int column; int row; POINT * pt;
\r
9217 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9218 pt->y = lineGap + row * (squareSize + lineGap);
\r
9220 pt->x = lineGap + column * (squareSize + lineGap);
\r
9221 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9225 /* Generate a series of frame coords from start->mid->finish.
\r
9226 The movement rate doubles until the half way point is
\r
9227 reached, then halves back down to the final destination,
\r
9228 which gives a nice slow in/out effect. The algorithmn
\r
9229 may seem to generate too many intermediates for short
\r
9230 moves, but remember that the purpose is to attract the
\r
9231 viewers attention to the piece about to be moved and
\r
9232 then to where it ends up. Too few frames would be less
\r
9236 Tween(start, mid, finish, factor, frames, nFrames)
\r
9237 POINT * start; POINT * mid;
\r
9238 POINT * finish; int factor;
\r
9239 POINT frames[]; int * nFrames;
\r
9241 int n, fraction = 1, count = 0;
\r
9243 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9244 for (n = 0; n < factor; n++)
\r
9246 for (n = 0; n < factor; n++) {
\r
9247 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9248 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9250 fraction = fraction / 2;
\r
9254 frames[count] = *mid;
\r
9257 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9259 for (n = 0; n < factor; n++) {
\r
9260 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9261 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9263 fraction = fraction * 2;
\r
9269 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9271 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9273 EvalGraphSet( first, last, current, pvInfoList );
\r