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
2009 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2012 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2013 if (appData.showButtonBar) {
\r
2014 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2015 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2017 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2019 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2020 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2022 boardRect.left = OUTER_MARGIN;
\r
2023 boardRect.right = boardRect.left + boardWidth;
\r
2024 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2025 boardRect.bottom = boardRect.top + boardHeight;
\r
2027 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2028 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2029 oldBoardSize = boardSize;
\r
2030 oldTinyLayout = tinyLayout;
\r
2031 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2032 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2033 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2034 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2035 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2036 wpMain.height = winH; // without disturbing window attachments
\r
2037 GetWindowRect(hwndMain, &wrect);
\r
2038 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2039 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2041 // [HGM] placement: let attached windows follow size change.
\r
2042 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2043 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2044 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2045 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2046 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2048 /* compensate if menu bar wrapped */
\r
2049 GetClientRect(hwndMain, &crect);
\r
2050 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2051 wpMain.height += offby;
\r
2053 case WMSZ_TOPLEFT:
\r
2054 SetWindowPos(hwndMain, NULL,
\r
2055 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2056 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2059 case WMSZ_TOPRIGHT:
\r
2061 SetWindowPos(hwndMain, NULL,
\r
2062 wrect.left, wrect.bottom - wpMain.height,
\r
2063 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2066 case WMSZ_BOTTOMLEFT:
\r
2068 SetWindowPos(hwndMain, NULL,
\r
2069 wrect.right - wpMain.width, wrect.top,
\r
2070 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2073 case WMSZ_BOTTOMRIGHT:
\r
2077 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2078 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2083 for (i = 0; i < N_BUTTONS; i++) {
\r
2084 if (buttonDesc[i].hwnd != NULL) {
\r
2085 DestroyWindow(buttonDesc[i].hwnd);
\r
2086 buttonDesc[i].hwnd = NULL;
\r
2088 if (appData.showButtonBar) {
\r
2089 buttonDesc[i].hwnd =
\r
2090 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2091 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2092 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2093 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2094 (HMENU) buttonDesc[i].id,
\r
2095 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2097 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2098 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2099 MAKELPARAM(FALSE, 0));
\r
2101 if (buttonDesc[i].id == IDM_Pause)
\r
2102 hwndPause = buttonDesc[i].hwnd;
\r
2103 buttonDesc[i].wndproc = (WNDPROC)
\r
2104 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2107 if (gridPen != NULL) DeleteObject(gridPen);
\r
2108 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2109 if (premovePen != NULL) DeleteObject(premovePen);
\r
2110 if (lineGap != 0) {
\r
2111 logbrush.lbStyle = BS_SOLID;
\r
2112 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2114 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2115 lineGap, &logbrush, 0, NULL);
\r
2116 logbrush.lbColor = highlightSquareColor;
\r
2118 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2119 lineGap, &logbrush, 0, NULL);
\r
2121 logbrush.lbColor = premoveHighlightColor;
\r
2123 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2124 lineGap, &logbrush, 0, NULL);
\r
2126 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2127 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2128 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2129 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2130 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2131 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2132 BOARD_WIDTH * (squareSize + lineGap);
\r
2133 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2135 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2136 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2137 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2138 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2139 lineGap / 2 + (i * (squareSize + lineGap));
\r
2140 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2141 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2142 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2146 /* [HGM] Licensing requirement */
\r
2148 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2151 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2153 GothicPopUp( "", VariantNormal);
\r
2156 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2158 /* Load piece bitmaps for this board size */
\r
2159 for (i=0; i<=2; i++) {
\r
2160 for (piece = WhitePawn;
\r
2161 (int) piece < (int) BlackPawn;
\r
2162 piece = (ChessSquare) ((int) piece + 1)) {
\r
2163 if (pieceBitmap[i][piece] != NULL)
\r
2164 DeleteObject(pieceBitmap[i][piece]);
\r
2168 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2169 // Orthodox Chess pieces
\r
2170 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2171 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2172 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2173 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2174 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2175 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2176 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2177 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2178 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2179 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2180 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2181 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2182 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2183 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2184 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2185 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2186 // in Shogi, Hijack the unused Queen for Lance
\r
2187 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2188 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2189 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2191 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2192 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2193 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2196 if(squareSize <= 72 && squareSize >= 33) {
\r
2197 /* A & C are available in most sizes now */
\r
2198 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2199 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2200 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2201 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2202 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2203 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2204 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2205 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2206 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2207 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2208 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2209 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2210 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2211 } else { // Smirf-like
\r
2212 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2216 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2217 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2218 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2219 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2220 } else { // WinBoard standard
\r
2221 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2222 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2223 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2228 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2229 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2232 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2233 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2234 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2235 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2236 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2237 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2238 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2241 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2242 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2243 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2244 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2245 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2246 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2247 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2248 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2249 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2250 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2260 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2261 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2262 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2263 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2264 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2265 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2266 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2267 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2268 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2269 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2270 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2271 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2272 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2280 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2281 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2282 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2283 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2284 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2285 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2288 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2289 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2290 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2291 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2292 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2293 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2294 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2295 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2296 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2297 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2298 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2299 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2300 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2301 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2302 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2306 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2307 /* special Shogi support in this size */
\r
2308 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2309 for (piece = WhitePawn;
\r
2310 (int) piece < (int) BlackPawn;
\r
2311 piece = (ChessSquare) ((int) piece + 1)) {
\r
2312 if (pieceBitmap[i][piece] != NULL)
\r
2313 DeleteObject(pieceBitmap[i][piece]);
\r
2316 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2317 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2318 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2319 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2320 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2321 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2322 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2323 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2324 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2325 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2326 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2327 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2328 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2330 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2331 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2332 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2333 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2334 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2335 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2336 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2337 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2338 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2339 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2340 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2341 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2342 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2344 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2345 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2346 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2347 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2348 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2349 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2350 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2351 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2352 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2353 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2354 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2355 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2356 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2363 PieceBitmap(ChessSquare p, int kind)
\r
2365 if ((int) p >= (int) BlackPawn)
\r
2366 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2368 return pieceBitmap[kind][(int) p];
\r
2371 /***************************************************************/
\r
2373 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2374 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2376 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2377 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2381 SquareToPos(int row, int column, int * x, int * y)
\r
2384 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2385 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2387 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2388 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2393 DrawCoordsOnDC(HDC hdc)
\r
2395 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
2396 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
2397 char str[2] = { NULLCHAR, NULLCHAR };
\r
2398 int oldMode, oldAlign, x, y, start, i;
\r
2402 if (!appData.showCoords)
\r
2405 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2407 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2408 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2409 oldAlign = GetTextAlign(hdc);
\r
2410 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2412 y = boardRect.top + lineGap;
\r
2413 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2415 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2416 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2417 str[0] = files[start + i];
\r
2418 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2419 y += squareSize + lineGap;
\r
2422 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2424 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2425 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2426 str[0] = ranks[start + i];
\r
2427 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2428 x += squareSize + lineGap;
\r
2431 SelectObject(hdc, oldBrush);
\r
2432 SetBkMode(hdc, oldMode);
\r
2433 SetTextAlign(hdc, oldAlign);
\r
2434 SelectObject(hdc, oldFont);
\r
2438 DrawGridOnDC(HDC hdc)
\r
2442 if (lineGap != 0) {
\r
2443 oldPen = SelectObject(hdc, gridPen);
\r
2444 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2445 SelectObject(hdc, oldPen);
\r
2449 #define HIGHLIGHT_PEN 0
\r
2450 #define PREMOVE_PEN 1
\r
2453 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2456 HPEN oldPen, hPen;
\r
2457 if (lineGap == 0) return;
\r
2459 x1 = boardRect.left +
\r
2460 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2461 y1 = boardRect.top +
\r
2462 lineGap/2 + y * (squareSize + lineGap);
\r
2464 x1 = boardRect.left +
\r
2465 lineGap/2 + x * (squareSize + lineGap);
\r
2466 y1 = boardRect.top +
\r
2467 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2469 hPen = pen ? premovePen : highlightPen;
\r
2470 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2471 MoveToEx(hdc, x1, y1, NULL);
\r
2472 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2473 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2474 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2475 LineTo(hdc, x1, y1);
\r
2476 SelectObject(hdc, oldPen);
\r
2480 DrawHighlightsOnDC(HDC hdc)
\r
2483 for (i=0; i<2; i++) {
\r
2484 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2485 DrawHighlightOnDC(hdc, TRUE,
\r
2486 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2489 for (i=0; i<2; i++) {
\r
2490 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2491 premoveHighlightInfo.sq[i].y >= 0) {
\r
2492 DrawHighlightOnDC(hdc, TRUE,
\r
2493 premoveHighlightInfo.sq[i].x,
\r
2494 premoveHighlightInfo.sq[i].y,
\r
2500 /* Note: sqcolor is used only in monoMode */
\r
2501 /* Note that this code is largely duplicated in woptions.c,
\r
2502 function DrawSampleSquare, so that needs to be updated too */
\r
2504 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2506 HBITMAP oldBitmap;
\r
2510 if (appData.blindfold) return;
\r
2512 /* [AS] Use font-based pieces if needed */
\r
2513 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2514 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2515 CreatePiecesFromFont();
\r
2517 if( fontBitmapSquareSize == squareSize ) {
\r
2518 int index = TranslatePieceToFontPiece(piece);
\r
2520 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2524 squareSize, squareSize,
\r
2529 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2533 squareSize, squareSize,
\r
2542 if (appData.monoMode) {
\r
2543 SelectObject(tmphdc, PieceBitmap(piece,
\r
2544 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2545 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2546 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2548 tmpSize = squareSize;
\r
2550 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2551 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2552 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2553 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2554 x += (squareSize - minorSize)>>1;
\r
2555 y += squareSize - minorSize - 2;
\r
2556 tmpSize = minorSize;
\r
2558 if (color || appData.allWhite ) {
\r
2559 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2561 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2562 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2563 if(appData.upsideDown && color==flipView)
\r
2564 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2566 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2567 /* Use black for outline of white pieces */
\r
2568 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2569 if(appData.upsideDown && color==flipView)
\r
2570 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2572 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2574 /* Use square color for details of black pieces */
\r
2575 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2576 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2577 if(appData.upsideDown && !flipView)
\r
2578 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2580 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2582 SelectObject(hdc, oldBrush);
\r
2583 SelectObject(tmphdc, oldBitmap);
\r
2587 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2588 int GetBackTextureMode( int algo )
\r
2590 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2594 case BACK_TEXTURE_MODE_PLAIN:
\r
2595 result = 1; /* Always use identity map */
\r
2597 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2598 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2606 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2607 to handle redraws cleanly (as random numbers would always be different).
\r
2609 VOID RebuildTextureSquareInfo()
\r
2619 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2621 if( liteBackTexture != NULL ) {
\r
2622 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2623 lite_w = bi.bmWidth;
\r
2624 lite_h = bi.bmHeight;
\r
2628 if( darkBackTexture != NULL ) {
\r
2629 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2630 dark_w = bi.bmWidth;
\r
2631 dark_h = bi.bmHeight;
\r
2635 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2636 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2637 if( (col + row) & 1 ) {
\r
2639 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2640 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2641 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2642 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2647 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2648 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2649 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2650 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2657 /* [AS] Arrow highlighting support */
\r
2659 static int A_WIDTH = 5; /* Width of arrow body */
\r
2661 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2662 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2664 static double Sqr( double x )
\r
2669 static int Round( double x )
\r
2671 return (int) (x + 0.5);
\r
2674 /* Draw an arrow between two points using current settings */
\r
2675 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2678 double dx, dy, j, k, x, y;
\r
2680 if( d_x == s_x ) {
\r
2681 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2683 arrow[0].x = s_x + A_WIDTH;
\r
2686 arrow[1].x = s_x + A_WIDTH;
\r
2687 arrow[1].y = d_y - h;
\r
2689 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2690 arrow[2].y = d_y - h;
\r
2695 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2696 arrow[4].y = d_y - h;
\r
2698 arrow[5].x = s_x - A_WIDTH;
\r
2699 arrow[5].y = d_y - h;
\r
2701 arrow[6].x = s_x - A_WIDTH;
\r
2704 else if( d_y == s_y ) {
\r
2705 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2708 arrow[0].y = s_y + A_WIDTH;
\r
2710 arrow[1].x = d_x - w;
\r
2711 arrow[1].y = s_y + A_WIDTH;
\r
2713 arrow[2].x = d_x - w;
\r
2714 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2719 arrow[4].x = d_x - w;
\r
2720 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2722 arrow[5].x = d_x - w;
\r
2723 arrow[5].y = s_y - A_WIDTH;
\r
2726 arrow[6].y = s_y - A_WIDTH;
\r
2729 /* [AS] Needed a lot of paper for this! :-) */
\r
2730 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2731 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2733 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2735 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2740 arrow[0].x = Round(x - j);
\r
2741 arrow[0].y = Round(y + j*dx);
\r
2743 arrow[1].x = Round(x + j);
\r
2744 arrow[1].y = Round(y - j*dx);
\r
2747 x = (double) d_x - k;
\r
2748 y = (double) d_y - k*dy;
\r
2751 x = (double) d_x + k;
\r
2752 y = (double) d_y + k*dy;
\r
2755 arrow[2].x = Round(x + j);
\r
2756 arrow[2].y = Round(y - j*dx);
\r
2758 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2759 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2764 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2765 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2767 arrow[6].x = Round(x - j);
\r
2768 arrow[6].y = Round(y + j*dx);
\r
2771 Polygon( hdc, arrow, 7 );
\r
2774 /* [AS] Draw an arrow between two squares */
\r
2775 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2777 int s_x, s_y, d_x, d_y;
\r
2784 if( s_col == d_col && s_row == d_row ) {
\r
2788 /* Get source and destination points */
\r
2789 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2790 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2793 d_y += squareSize / 4;
\r
2795 else if( d_y < s_y ) {
\r
2796 d_y += 3 * squareSize / 4;
\r
2799 d_y += squareSize / 2;
\r
2803 d_x += squareSize / 4;
\r
2805 else if( d_x < s_x ) {
\r
2806 d_x += 3 * squareSize / 4;
\r
2809 d_x += squareSize / 2;
\r
2812 s_x += squareSize / 2;
\r
2813 s_y += squareSize / 2;
\r
2815 /* Adjust width */
\r
2816 A_WIDTH = squareSize / 14;
\r
2819 stLB.lbStyle = BS_SOLID;
\r
2820 stLB.lbColor = appData.highlightArrowColor;
\r
2823 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2824 holdpen = SelectObject( hdc, hpen );
\r
2825 hbrush = CreateBrushIndirect( &stLB );
\r
2826 holdbrush = SelectObject( hdc, hbrush );
\r
2828 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2830 SelectObject( hdc, holdpen );
\r
2831 SelectObject( hdc, holdbrush );
\r
2832 DeleteObject( hpen );
\r
2833 DeleteObject( hbrush );
\r
2836 BOOL HasHighlightInfo()
\r
2838 BOOL result = FALSE;
\r
2840 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2841 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2849 BOOL IsDrawArrowEnabled()
\r
2851 BOOL result = FALSE;
\r
2853 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2860 VOID DrawArrowHighlight( HDC hdc )
\r
2862 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2863 DrawArrowBetweenSquares( hdc,
\r
2864 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2865 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2869 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2871 HRGN result = NULL;
\r
2873 if( HasHighlightInfo() ) {
\r
2874 int x1, y1, x2, y2;
\r
2875 int sx, sy, dx, dy;
\r
2877 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2878 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2880 sx = MIN( x1, x2 );
\r
2881 sy = MIN( y1, y2 );
\r
2882 dx = MAX( x1, x2 ) + squareSize;
\r
2883 dy = MAX( y1, y2 ) + squareSize;
\r
2885 result = CreateRectRgn( sx, sy, dx, dy );
\r
2892 Warning: this function modifies the behavior of several other functions.
\r
2894 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2895 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2896 repaint is scattered all over the place, which is not good for features such as
\r
2897 "arrow highlighting" that require a full repaint of the board.
\r
2899 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2900 user interaction, when speed is not so important) but especially to avoid errors
\r
2901 in the displayed graphics.
\r
2903 In such patched places, I always try refer to this function so there is a single
\r
2904 place to maintain knowledge.
\r
2906 To restore the original behavior, just return FALSE unconditionally.
\r
2908 BOOL IsFullRepaintPreferrable()
\r
2910 BOOL result = FALSE;
\r
2912 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2913 /* Arrow may appear on the board */
\r
2921 This function is called by DrawPosition to know whether a full repaint must
\r
2924 Only DrawPosition may directly call this function, which makes use of
\r
2925 some state information. Other function should call DrawPosition specifying
\r
2926 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2928 BOOL DrawPositionNeedsFullRepaint()
\r
2930 BOOL result = FALSE;
\r
2933 Probably a slightly better policy would be to trigger a full repaint
\r
2934 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2935 but animation is fast enough that it's difficult to notice.
\r
2937 if( animInfo.piece == EmptySquare ) {
\r
2938 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2947 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2949 int row, column, x, y, square_color, piece_color;
\r
2950 ChessSquare piece;
\r
2952 HDC texture_hdc = NULL;
\r
2954 /* [AS] Initialize background textures if needed */
\r
2955 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2956 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2957 if( backTextureSquareSize != squareSize
\r
2958 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2959 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2960 backTextureSquareSize = squareSize;
\r
2961 RebuildTextureSquareInfo();
\r
2964 texture_hdc = CreateCompatibleDC( hdc );
\r
2967 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2968 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2970 SquareToPos(row, column, &x, &y);
\r
2972 piece = board[row][column];
\r
2974 square_color = ((column + row) % 2) == 1;
\r
2975 if( gameInfo.variant == VariantXiangqi ) {
\r
2976 square_color = !InPalace(row, column);
\r
2977 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2978 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2980 piece_color = (int) piece < (int) BlackPawn;
\r
2983 /* [HGM] holdings file: light square or black */
\r
2984 if(column == BOARD_LEFT-2) {
\r
2985 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2988 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
2992 if(column == BOARD_RGHT + 1 ) {
\r
2993 if( row < gameInfo.holdingsSize )
\r
2996 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3000 if(column == BOARD_LEFT-1 ) /* left align */
\r
3001 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3002 else if( column == BOARD_RGHT) /* right align */
\r
3003 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3005 if (appData.monoMode) {
\r
3006 if (piece == EmptySquare) {
\r
3007 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3008 square_color ? WHITENESS : BLACKNESS);
\r
3010 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3013 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3014 /* [AS] Draw the square using a texture bitmap */
\r
3015 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3016 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3017 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3020 squareSize, squareSize,
\r
3023 backTextureSquareInfo[r][c].mode,
\r
3024 backTextureSquareInfo[r][c].x,
\r
3025 backTextureSquareInfo[r][c].y );
\r
3027 SelectObject( texture_hdc, hbm );
\r
3029 if (piece != EmptySquare) {
\r
3030 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3034 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3036 oldBrush = SelectObject(hdc, brush );
\r
3037 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3038 SelectObject(hdc, oldBrush);
\r
3039 if (piece != EmptySquare)
\r
3040 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3045 if( texture_hdc != NULL ) {
\r
3046 DeleteDC( texture_hdc );
\r
3050 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3051 void fputDW(FILE *f, int x)
\r
3053 fputc(x & 255, f);
\r
3054 fputc(x>>8 & 255, f);
\r
3055 fputc(x>>16 & 255, f);
\r
3056 fputc(x>>24 & 255, f);
\r
3059 #define MAX_CLIPS 200 /* more than enough */
\r
3062 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3064 // HBITMAP bufferBitmap;
\r
3069 int w = 100, h = 50;
\r
3071 if(logo == NULL) return;
\r
3072 // GetClientRect(hwndMain, &Rect);
\r
3073 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3074 // Rect.bottom-Rect.top+1);
\r
3075 tmphdc = CreateCompatibleDC(hdc);
\r
3076 hbm = SelectObject(tmphdc, logo);
\r
3077 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3081 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3082 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3083 SelectObject(tmphdc, hbm);
\r
3088 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3090 static Board lastReq, lastDrawn;
\r
3091 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3092 static int lastDrawnFlipView = 0;
\r
3093 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3094 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3097 HBITMAP bufferBitmap;
\r
3098 HBITMAP oldBitmap;
\r
3100 HRGN clips[MAX_CLIPS];
\r
3101 ChessSquare dragged_piece = EmptySquare;
\r
3103 /* I'm undecided on this - this function figures out whether a full
\r
3104 * repaint is necessary on its own, so there's no real reason to have the
\r
3105 * caller tell it that. I think this can safely be set to FALSE - but
\r
3106 * if we trust the callers not to request full repaints unnessesarily, then
\r
3107 * we could skip some clipping work. In other words, only request a full
\r
3108 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3109 * gamestart and similar) --Hawk
\r
3111 Boolean fullrepaint = repaint;
\r
3113 if( DrawPositionNeedsFullRepaint() ) {
\r
3114 fullrepaint = TRUE;
\r
3117 if (board == NULL) {
\r
3118 if (!lastReqValid) {
\r
3123 CopyBoard(lastReq, board);
\r
3127 if (doingSizing) {
\r
3131 if (IsIconic(hwndMain)) {
\r
3135 if (hdc == NULL) {
\r
3136 hdc = GetDC(hwndMain);
\r
3137 if (!appData.monoMode) {
\r
3138 SelectPalette(hdc, hPal, FALSE);
\r
3139 RealizePalette(hdc);
\r
3143 releaseDC = FALSE;
\r
3146 /* Create some work-DCs */
\r
3147 hdcmem = CreateCompatibleDC(hdc);
\r
3148 tmphdc = CreateCompatibleDC(hdc);
\r
3150 /* If dragging is in progress, we temporarely remove the piece */
\r
3151 /* [HGM] or temporarily decrease count if stacked */
\r
3152 /* !! Moved to before board compare !! */
\r
3153 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3154 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3155 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3156 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3157 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3159 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3160 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3161 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3163 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3166 /* Figure out which squares need updating by comparing the
\r
3167 * newest board with the last drawn board and checking if
\r
3168 * flipping has changed.
\r
3170 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3171 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3172 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3173 if (lastDrawn[row][column] != board[row][column]) {
\r
3174 SquareToPos(row, column, &x, &y);
\r
3175 clips[num_clips++] =
\r
3176 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3180 for (i=0; i<2; i++) {
\r
3181 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3182 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3183 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3184 lastDrawnHighlight.sq[i].y >= 0) {
\r
3185 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3186 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3187 clips[num_clips++] =
\r
3188 CreateRectRgn(x - lineGap, y - lineGap,
\r
3189 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3191 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3192 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3193 clips[num_clips++] =
\r
3194 CreateRectRgn(x - lineGap, y - lineGap,
\r
3195 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3199 for (i=0; i<2; i++) {
\r
3200 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3201 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3202 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3203 lastDrawnPremove.sq[i].y >= 0) {
\r
3204 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3205 lastDrawnPremove.sq[i].x, &x, &y);
\r
3206 clips[num_clips++] =
\r
3207 CreateRectRgn(x - lineGap, y - lineGap,
\r
3208 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3210 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3211 premoveHighlightInfo.sq[i].y >= 0) {
\r
3212 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3213 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3214 clips[num_clips++] =
\r
3215 CreateRectRgn(x - lineGap, y - lineGap,
\r
3216 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3221 fullrepaint = TRUE;
\r
3224 /* Create a buffer bitmap - this is the actual bitmap
\r
3225 * being written to. When all the work is done, we can
\r
3226 * copy it to the real DC (the screen). This avoids
\r
3227 * the problems with flickering.
\r
3229 GetClientRect(hwndMain, &Rect);
\r
3230 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3231 Rect.bottom-Rect.top+1);
\r
3232 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3233 if (!appData.monoMode) {
\r
3234 SelectPalette(hdcmem, hPal, FALSE);
\r
3237 /* Create clips for dragging */
\r
3238 if (!fullrepaint) {
\r
3239 if (dragInfo.from.x >= 0) {
\r
3240 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3241 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3243 if (dragInfo.start.x >= 0) {
\r
3244 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3245 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3247 if (dragInfo.pos.x >= 0) {
\r
3248 x = dragInfo.pos.x - squareSize / 2;
\r
3249 y = dragInfo.pos.y - squareSize / 2;
\r
3250 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3252 if (dragInfo.lastpos.x >= 0) {
\r
3253 x = dragInfo.lastpos.x - squareSize / 2;
\r
3254 y = dragInfo.lastpos.y - squareSize / 2;
\r
3255 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3259 /* Are we animating a move?
\r
3261 * - remove the piece from the board (temporarely)
\r
3262 * - calculate the clipping region
\r
3264 if (!fullrepaint) {
\r
3265 if (animInfo.piece != EmptySquare) {
\r
3266 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3267 x = boardRect.left + animInfo.lastpos.x;
\r
3268 y = boardRect.top + animInfo.lastpos.y;
\r
3269 x2 = boardRect.left + animInfo.pos.x;
\r
3270 y2 = boardRect.top + animInfo.pos.y;
\r
3271 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3272 /* Slight kludge. The real problem is that after AnimateMove is
\r
3273 done, the position on the screen does not match lastDrawn.
\r
3274 This currently causes trouble only on e.p. captures in
\r
3275 atomic, where the piece moves to an empty square and then
\r
3276 explodes. The old and new positions both had an empty square
\r
3277 at the destination, but animation has drawn a piece there and
\r
3278 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3279 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3283 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3284 if (num_clips == 0)
\r
3285 fullrepaint = TRUE;
\r
3287 /* Set clipping on the memory DC */
\r
3288 if (!fullrepaint) {
\r
3289 SelectClipRgn(hdcmem, clips[0]);
\r
3290 for (x = 1; x < num_clips; x++) {
\r
3291 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3292 abort(); // this should never ever happen!
\r
3296 /* Do all the drawing to the memory DC */
\r
3297 if(explodeInfo.radius) { // [HGM] atomic
\r
3299 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3300 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3301 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3302 x += squareSize/2;
\r
3303 y += squareSize/2;
\r
3304 if(!fullrepaint) {
\r
3305 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3306 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3308 DrawGridOnDC(hdcmem);
\r
3309 DrawHighlightsOnDC(hdcmem);
\r
3310 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3311 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3312 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3313 SelectObject(hdcmem, oldBrush);
\r
3315 DrawGridOnDC(hdcmem);
\r
3316 DrawHighlightsOnDC(hdcmem);
\r
3317 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3319 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3320 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3321 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3322 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3323 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3324 SquareToPos(row, column, &x, &y);
\r
3325 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3326 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3327 SelectObject(hdcmem, oldBrush);
\r
3332 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3333 if(appData.autoLogo) {
\r
3335 switch(gameMode) { // pick logos based on game mode
\r
3336 case IcsObserving:
\r
3337 whiteLogo = second.programLogo; // ICS logo
\r
3338 blackLogo = second.programLogo;
\r
3341 case IcsPlayingWhite:
\r
3342 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3343 blackLogo = second.programLogo; // ICS logo
\r
3345 case IcsPlayingBlack:
\r
3346 whiteLogo = second.programLogo; // ICS logo
\r
3347 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3349 case TwoMachinesPlay:
\r
3350 if(first.twoMachinesColor[0] == 'b') {
\r
3351 whiteLogo = second.programLogo;
\r
3352 blackLogo = first.programLogo;
\r
3355 case MachinePlaysWhite:
\r
3356 blackLogo = userLogo;
\r
3358 case MachinePlaysBlack:
\r
3359 whiteLogo = userLogo;
\r
3360 blackLogo = first.programLogo;
\r
3363 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3364 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3367 if( appData.highlightMoveWithArrow ) {
\r
3368 DrawArrowHighlight(hdcmem);
\r
3371 DrawCoordsOnDC(hdcmem);
\r
3373 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3374 /* to make sure lastDrawn contains what is actually drawn */
\r
3376 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3377 if (dragged_piece != EmptySquare) {
\r
3378 /* [HGM] or restack */
\r
3379 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3380 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3382 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3383 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3384 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3385 x = dragInfo.pos.x - squareSize / 2;
\r
3386 y = dragInfo.pos.y - squareSize / 2;
\r
3387 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3388 ((int) dragged_piece < (int) BlackPawn),
\r
3389 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3392 /* Put the animated piece back into place and draw it */
\r
3393 if (animInfo.piece != EmptySquare) {
\r
3394 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3395 x = boardRect.left + animInfo.pos.x;
\r
3396 y = boardRect.top + animInfo.pos.y;
\r
3397 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3398 ((int) animInfo.piece < (int) BlackPawn),
\r
3399 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3402 /* Release the bufferBitmap by selecting in the old bitmap
\r
3403 * and delete the memory DC
\r
3405 SelectObject(hdcmem, oldBitmap);
\r
3408 /* Set clipping on the target DC */
\r
3409 if (!fullrepaint) {
\r
3410 SelectClipRgn(hdc, clips[0]);
\r
3411 for (x = 1; x < num_clips; x++) {
\r
3412 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3413 abort(); // this should never ever happen!
\r
3417 /* Copy the new bitmap onto the screen in one go.
\r
3418 * This way we avoid any flickering
\r
3420 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3421 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3422 boardRect.right - boardRect.left,
\r
3423 boardRect.bottom - boardRect.top,
\r
3424 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3425 if(saveDiagFlag) {
\r
3426 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3427 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3429 GetObject(bufferBitmap, sizeof(b), &b);
\r
3430 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3431 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3432 bih.biWidth = b.bmWidth;
\r
3433 bih.biHeight = b.bmHeight;
\r
3435 bih.biBitCount = b.bmBitsPixel;
\r
3436 bih.biCompression = 0;
\r
3437 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3438 bih.biXPelsPerMeter = 0;
\r
3439 bih.biYPelsPerMeter = 0;
\r
3440 bih.biClrUsed = 0;
\r
3441 bih.biClrImportant = 0;
\r
3442 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3443 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3444 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3445 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3447 wb = b.bmWidthBytes;
\r
3449 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3450 int k = ((int*) pData)[i];
\r
3451 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3452 if(j >= 16) break;
\r
3454 if(j >= nrColors) nrColors = j+1;
\r
3456 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3458 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3459 for(w=0; w<(wb>>2); w+=2) {
\r
3460 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3461 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3462 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3463 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3464 pData[p++] = m | j<<4;
\r
3466 while(p&3) pData[p++] = 0;
\r
3469 wb = ((wb+31)>>5)<<2;
\r
3471 // write BITMAPFILEHEADER
\r
3472 fprintf(diagFile, "BM");
\r
3473 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3474 fputDW(diagFile, 0);
\r
3475 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3476 // write BITMAPINFOHEADER
\r
3477 fputDW(diagFile, 40);
\r
3478 fputDW(diagFile, b.bmWidth);
\r
3479 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3480 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3481 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3482 fputDW(diagFile, 0);
\r
3483 fputDW(diagFile, 0);
\r
3484 fputDW(diagFile, 0);
\r
3485 fputDW(diagFile, 0);
\r
3486 fputDW(diagFile, 0);
\r
3487 fputDW(diagFile, 0);
\r
3488 // write color table
\r
3490 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3491 // write bitmap data
\r
3492 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3493 fputc(pData[i], diagFile);
\r
3497 SelectObject(tmphdc, oldBitmap);
\r
3499 /* Massive cleanup */
\r
3500 for (x = 0; x < num_clips; x++)
\r
3501 DeleteObject(clips[x]);
\r
3504 DeleteObject(bufferBitmap);
\r
3507 ReleaseDC(hwndMain, hdc);
\r
3509 if (lastDrawnFlipView != flipView) {
\r
3511 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3513 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3516 /* CopyBoard(lastDrawn, board);*/
\r
3517 lastDrawnHighlight = highlightInfo;
\r
3518 lastDrawnPremove = premoveHighlightInfo;
\r
3519 lastDrawnFlipView = flipView;
\r
3520 lastDrawnValid = 1;
\r
3523 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3528 saveDiagFlag = 1; diagFile = f;
\r
3529 HDCDrawPosition(NULL, TRUE, NULL);
\r
3533 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3540 /*---------------------------------------------------------------------------*\
\r
3541 | CLIENT PAINT PROCEDURE
\r
3542 | This is the main event-handler for the WM_PAINT message.
\r
3544 \*---------------------------------------------------------------------------*/
\r
3546 PaintProc(HWND hwnd)
\r
3552 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3553 if (IsIconic(hwnd)) {
\r
3554 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3556 if (!appData.monoMode) {
\r
3557 SelectPalette(hdc, hPal, FALSE);
\r
3558 RealizePalette(hdc);
\r
3560 HDCDrawPosition(hdc, 1, NULL);
\r
3562 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3563 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3564 ETO_CLIPPED|ETO_OPAQUE,
\r
3565 &messageRect, messageText, strlen(messageText), NULL);
\r
3566 SelectObject(hdc, oldFont);
\r
3567 DisplayBothClocks();
\r
3569 EndPaint(hwnd,&ps);
\r
3577 * If the user selects on a border boundary, return -1; if off the board,
\r
3578 * return -2. Otherwise map the event coordinate to the square.
\r
3579 * The offset boardRect.left or boardRect.top must already have been
\r
3580 * subtracted from x.
\r
3582 int EventToSquare(x, limit)
\r
3590 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3592 x /= (squareSize + lineGap);
\r
3604 DropEnable dropEnables[] = {
\r
3605 { 'P', DP_Pawn, "Pawn" },
\r
3606 { 'N', DP_Knight, "Knight" },
\r
3607 { 'B', DP_Bishop, "Bishop" },
\r
3608 { 'R', DP_Rook, "Rook" },
\r
3609 { 'Q', DP_Queen, "Queen" },
\r
3613 SetupDropMenu(HMENU hmenu)
\r
3615 int i, count, enable;
\r
3617 extern char white_holding[], black_holding[];
\r
3618 char item[MSG_SIZ];
\r
3620 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3621 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3622 dropEnables[i].piece);
\r
3624 while (p && *p++ == dropEnables[i].piece) count++;
\r
3625 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3626 enable = count > 0 || !appData.testLegality
\r
3627 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3628 && !appData.icsActive);
\r
3629 ModifyMenu(hmenu, dropEnables[i].command,
\r
3630 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3631 dropEnables[i].command, item);
\r
3635 void DragPieceBegin(int x, int y)
\r
3637 dragInfo.lastpos.x = boardRect.left + x;
\r
3638 dragInfo.lastpos.y = boardRect.top + y;
\r
3639 dragInfo.from.x = fromX;
\r
3640 dragInfo.from.y = fromY;
\r
3641 dragInfo.start = dragInfo.from;
\r
3642 SetCapture(hwndMain);
\r
3645 void DragPieceEnd(int x, int y)
\r
3648 dragInfo.start.x = dragInfo.start.y = -1;
\r
3649 dragInfo.from = dragInfo.start;
\r
3650 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3653 /* Event handler for mouse messages */
\r
3655 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3659 static int recursive = 0;
\r
3661 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3664 if (message == WM_MBUTTONUP) {
\r
3665 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3666 to the middle button: we simulate pressing the left button too!
\r
3668 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3669 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3675 pt.x = LOWORD(lParam);
\r
3676 pt.y = HIWORD(lParam);
\r
3677 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3678 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3679 if (!flipView && y >= 0) {
\r
3680 y = BOARD_HEIGHT - 1 - y;
\r
3682 if (flipView && x >= 0) {
\r
3683 x = BOARD_WIDTH - 1 - x;
\r
3686 switch (message) {
\r
3687 case WM_LBUTTONDOWN:
\r
3688 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3689 if (gameMode == EditPosition) {
\r
3690 SetWhiteToPlayEvent();
\r
3691 } else if (gameMode == IcsPlayingBlack ||
\r
3692 gameMode == MachinePlaysWhite) {
\r
3694 } else if (gameMode == EditGame) {
\r
3695 AdjustClock(flipClock, -1);
\r
3697 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3698 if (gameMode == EditPosition) {
\r
3699 SetBlackToPlayEvent();
\r
3700 } else if (gameMode == IcsPlayingWhite ||
\r
3701 gameMode == MachinePlaysBlack) {
\r
3703 } else if (gameMode == EditGame) {
\r
3704 AdjustClock(!flipClock, -1);
\r
3707 dragInfo.start.x = dragInfo.start.y = -1;
\r
3708 dragInfo.from = dragInfo.start;
\r
3709 if(fromX == -1 && frozen) { // not sure where this is for
\r
3710 fromX = fromY = -1;
\r
3711 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3714 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3715 DrawPosition(TRUE, NULL);
\r
3718 case WM_LBUTTONUP:
\r
3719 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3720 DrawPosition(TRUE, NULL);
\r
3723 case WM_MOUSEMOVE:
\r
3724 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3725 if ((appData.animateDragging || appData.highlightDragging)
\r
3726 && (wParam & MK_LBUTTON)
\r
3727 && dragInfo.from.x >= 0)
\r
3729 BOOL full_repaint = FALSE;
\r
3731 if (appData.animateDragging) {
\r
3732 dragInfo.pos = pt;
\r
3734 if (appData.highlightDragging) {
\r
3735 SetHighlights(fromX, fromY, x, y);
\r
3736 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3737 full_repaint = TRUE;
\r
3741 DrawPosition( full_repaint, NULL);
\r
3743 dragInfo.lastpos = dragInfo.pos;
\r
3747 case WM_MOUSEWHEEL: // [DM]
\r
3748 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3749 /* Mouse Wheel is being rolled forward
\r
3750 * Play moves forward
\r
3752 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3753 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3754 /* Mouse Wheel is being rolled backward
\r
3755 * Play moves backward
\r
3757 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3758 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3762 case WM_MBUTTONUP:
\r
3763 case WM_RBUTTONUP:
\r
3765 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3768 case WM_MBUTTONDOWN:
\r
3769 case WM_RBUTTONDOWN:
\r
3772 fromX = fromY = -1;
\r
3773 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3774 dragInfo.start.x = dragInfo.start.y = -1;
\r
3775 dragInfo.from = dragInfo.start;
\r
3776 dragInfo.lastpos = dragInfo.pos;
\r
3777 if (appData.highlightDragging) {
\r
3778 ClearHighlights();
\r
3781 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3782 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3783 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3784 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3785 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3788 DrawPosition(TRUE, NULL);
\r
3790 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3793 if (message == WM_MBUTTONDOWN) {
\r
3794 buttonCount = 3; /* even if system didn't think so */
\r
3795 if (wParam & MK_SHIFT)
\r
3796 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3798 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3799 } else { /* message == WM_RBUTTONDOWN */
\r
3800 /* Just have one menu, on the right button. Windows users don't
\r
3801 think to try the middle one, and sometimes other software steals
\r
3802 it, or it doesn't really exist. */
\r
3803 if(gameInfo.variant != VariantShogi)
\r
3804 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3806 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3810 SetCapture(hwndMain);
3813 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3814 SetupDropMenu(hmenu);
\r
3815 MenuPopup(hwnd, pt, hmenu, -1);
\r
3825 /* Preprocess messages for buttons in main window */
\r
3827 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3829 int id = GetWindowLong(hwnd, GWL_ID);
\r
3832 for (i=0; i<N_BUTTONS; i++) {
\r
3833 if (buttonDesc[i].id == id) break;
\r
3835 if (i == N_BUTTONS) return 0;
\r
3836 switch (message) {
\r
3841 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3842 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3849 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3852 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3853 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3854 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3855 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3857 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3859 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3860 PopUpMoveDialog((char)wParam);
\r
3866 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3869 /* Process messages for Promotion dialog box */
\r
3871 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3875 switch (message) {
\r
3876 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3877 /* Center the dialog over the application window */
\r
3878 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3879 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3880 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3881 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3882 SW_SHOW : SW_HIDE);
\r
3883 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3884 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3885 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3886 PieceToChar(WhiteAngel) != '~') ||
\r
3887 (PieceToChar(BlackAngel) >= 'A' &&
\r
3888 PieceToChar(BlackAngel) != '~') ) ?
\r
3889 SW_SHOW : SW_HIDE);
\r
3890 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3891 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3892 PieceToChar(WhiteMarshall) != '~') ||
\r
3893 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3894 PieceToChar(BlackMarshall) != '~') ) ?
\r
3895 SW_SHOW : SW_HIDE);
\r
3896 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3897 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3898 gameInfo.variant != VariantShogi ?
\r
3899 SW_SHOW : SW_HIDE);
\r
3900 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3901 gameInfo.variant != VariantShogi ?
\r
3902 SW_SHOW : SW_HIDE);
\r
3903 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3904 gameInfo.variant == VariantShogi ?
\r
3905 SW_SHOW : SW_HIDE);
\r
3906 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3907 gameInfo.variant == VariantShogi ?
\r
3908 SW_SHOW : SW_HIDE);
\r
3909 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3910 gameInfo.variant == VariantSuper ?
\r
3911 SW_SHOW : SW_HIDE);
\r
3914 case WM_COMMAND: /* message: received a command */
\r
3915 switch (LOWORD(wParam)) {
\r
3917 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3918 ClearHighlights();
\r
3919 DrawPosition(FALSE, NULL);
\r
3922 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3925 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3928 promoChar = PieceToChar(BlackRook);
\r
3931 promoChar = PieceToChar(BlackBishop);
\r
3933 case PB_Chancellor:
\r
3934 promoChar = PieceToChar(BlackMarshall);
\r
3936 case PB_Archbishop:
\r
3937 promoChar = PieceToChar(BlackAngel);
\r
3940 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
3945 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3946 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
3947 only show the popup when we are already sure the move is valid or
\r
3948 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
3949 will figure out it is a promotion from the promoChar. */
\r
3950 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
3951 fromX = fromY = -1;
\r
3952 if (!appData.highlightLastMove) {
\r
3953 ClearHighlights();
\r
3954 DrawPosition(FALSE, NULL);
\r
3961 /* Pop up promotion dialog */
\r
3963 PromotionPopup(HWND hwnd)
\r
3967 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
3968 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
3969 hwnd, (DLGPROC)lpProc);
\r
3970 FreeProcInstance(lpProc);
\r
3976 DrawPosition(TRUE, NULL);
\r
3977 PromotionPopup(hwndMain);
\r
3980 /* Toggle ShowThinking */
\r
3982 ToggleShowThinking()
\r
3984 appData.showThinking = !appData.showThinking;
\r
3985 ShowThinkingEvent();
\r
3989 LoadGameDialog(HWND hwnd, char* title)
\r
3993 char fileTitle[MSG_SIZ];
\r
3994 f = OpenFileDialog(hwnd, "rb", "",
\r
3995 appData.oldSaveStyle ? "gam" : "pgn",
\r
3997 title, &number, fileTitle, NULL);
\r
3999 cmailMsgLoaded = FALSE;
\r
4000 if (number == 0) {
\r
4001 int error = GameListBuild(f);
\r
4003 DisplayError("Cannot build game list", error);
\r
4004 } else if (!ListEmpty(&gameList) &&
\r
4005 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4006 GameListPopUp(f, fileTitle);
\r
4009 GameListDestroy();
\r
4012 LoadGame(f, number, fileTitle, FALSE);
\r
4016 int get_term_width()
\r
4021 HFONT hfont, hold_font;
\r
4026 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4030 // get the text metrics
\r
4031 hdc = GetDC(hText);
\r
4032 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4033 if (consoleCF.dwEffects & CFE_BOLD)
\r
4034 lf.lfWeight = FW_BOLD;
\r
4035 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4036 lf.lfItalic = TRUE;
\r
4037 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4038 lf.lfStrikeOut = TRUE;
\r
4039 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4040 lf.lfUnderline = TRUE;
\r
4041 hfont = CreateFontIndirect(&lf);
\r
4042 hold_font = SelectObject(hdc, hfont);
\r
4043 GetTextMetrics(hdc, &tm);
\r
4044 SelectObject(hdc, hold_font);
\r
4045 DeleteObject(hfont);
\r
4046 ReleaseDC(hText, hdc);
\r
4048 // get the rectangle
\r
4049 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4051 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4054 void UpdateICSWidth(HWND hText)
\r
4056 LONG old_width, new_width;
\r
4058 new_width = get_term_width(hText, FALSE);
\r
4059 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4060 if (new_width != old_width)
\r
4062 ics_update_width(new_width);
\r
4063 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4068 ChangedConsoleFont()
\r
4071 CHARRANGE tmpsel, sel;
\r
4072 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4073 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4074 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4077 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4078 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4079 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4080 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4081 * size. This was undocumented in the version of MSVC++ that I had
\r
4082 * when I wrote the code, but is apparently documented now.
\r
4084 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4085 cfmt.bCharSet = f->lf.lfCharSet;
\r
4086 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4087 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4088 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4089 /* Why are the following seemingly needed too? */
\r
4090 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4091 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4092 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4094 tmpsel.cpMax = -1; /*999999?*/
\r
4095 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4096 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4097 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4098 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4100 paraf.cbSize = sizeof(paraf);
\r
4101 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4102 paraf.dxStartIndent = 0;
\r
4103 paraf.dxOffset = WRAP_INDENT;
\r
4104 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4105 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4106 UpdateICSWidth(hText);
\r
4109 /*---------------------------------------------------------------------------*\
\r
4111 * Window Proc for main window
\r
4113 \*---------------------------------------------------------------------------*/
\r
4115 /* Process messages for main window, etc. */
\r
4117 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4120 int wmId, wmEvent;
\r
4124 char fileTitle[MSG_SIZ];
\r
4125 char buf[MSG_SIZ];
\r
4126 static SnapData sd;
\r
4128 switch (message) {
\r
4130 case WM_PAINT: /* message: repaint portion of window */
\r
4134 case WM_ERASEBKGND:
\r
4135 if (IsIconic(hwnd)) {
\r
4136 /* Cheat; change the message */
\r
4137 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4139 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4143 case WM_LBUTTONDOWN:
\r
4144 case WM_MBUTTONDOWN:
\r
4145 case WM_RBUTTONDOWN:
\r
4146 case WM_LBUTTONUP:
\r
4147 case WM_MBUTTONUP:
\r
4148 case WM_RBUTTONUP:
\r
4149 case WM_MOUSEMOVE:
\r
4150 case WM_MOUSEWHEEL:
\r
4151 MouseEvent(hwnd, message, wParam, lParam);
\r
4154 JAWS_KB_NAVIGATION
\r
4158 JAWS_ALT_INTERCEPT
\r
4160 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4161 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4162 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4163 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4165 SendMessage(h, message, wParam, lParam);
\r
4166 } else if(lParam != KF_REPEAT) {
\r
4167 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4168 PopUpMoveDialog((char)wParam);
\r
4169 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4170 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4175 case WM_PALETTECHANGED:
\r
4176 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4178 HDC hdc = GetDC(hwndMain);
\r
4179 SelectPalette(hdc, hPal, TRUE);
\r
4180 nnew = RealizePalette(hdc);
\r
4182 paletteChanged = TRUE;
\r
4183 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4185 ReleaseDC(hwnd, hdc);
\r
4189 case WM_QUERYNEWPALETTE:
\r
4190 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4192 HDC hdc = GetDC(hwndMain);
\r
4193 paletteChanged = FALSE;
\r
4194 SelectPalette(hdc, hPal, FALSE);
\r
4195 nnew = RealizePalette(hdc);
\r
4197 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4199 ReleaseDC(hwnd, hdc);
\r
4204 case WM_COMMAND: /* message: command from application menu */
\r
4205 wmId = LOWORD(wParam);
\r
4206 wmEvent = HIWORD(wParam);
\r
4211 SAY("new game enter a move to play against the computer with white");
\r
4214 case IDM_NewGameFRC:
\r
4215 if( NewGameFRC() == 0 ) {
\r
4220 case IDM_NewVariant:
\r
4221 NewVariantPopup(hwnd);
\r
4224 case IDM_LoadGame:
\r
4225 LoadGameDialog(hwnd, "Load Game from File");
\r
4228 case IDM_LoadNextGame:
\r
4232 case IDM_LoadPrevGame:
\r
4236 case IDM_ReloadGame:
\r
4240 case IDM_LoadPosition:
\r
4241 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4242 Reset(FALSE, TRUE);
\r
4245 f = OpenFileDialog(hwnd, "rb", "",
\r
4246 appData.oldSaveStyle ? "pos" : "fen",
\r
4248 "Load Position from File", &number, fileTitle, NULL);
\r
4250 LoadPosition(f, number, fileTitle);
\r
4254 case IDM_LoadNextPosition:
\r
4255 ReloadPosition(1);
\r
4258 case IDM_LoadPrevPosition:
\r
4259 ReloadPosition(-1);
\r
4262 case IDM_ReloadPosition:
\r
4263 ReloadPosition(0);
\r
4266 case IDM_SaveGame:
\r
4267 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4268 f = OpenFileDialog(hwnd, "a", defName,
\r
4269 appData.oldSaveStyle ? "gam" : "pgn",
\r
4271 "Save Game to File", NULL, fileTitle, NULL);
\r
4273 SaveGame(f, 0, "");
\r
4277 case IDM_SavePosition:
\r
4278 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4279 f = OpenFileDialog(hwnd, "a", defName,
\r
4280 appData.oldSaveStyle ? "pos" : "fen",
\r
4282 "Save Position to File", NULL, fileTitle, NULL);
\r
4284 SavePosition(f, 0, "");
\r
4288 case IDM_SaveDiagram:
\r
4289 defName = "diagram";
\r
4290 f = OpenFileDialog(hwnd, "wb", defName,
\r
4293 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4299 case IDM_CopyGame:
\r
4300 CopyGameToClipboard();
\r
4303 case IDM_PasteGame:
\r
4304 PasteGameFromClipboard();
\r
4307 case IDM_CopyGameListToClipboard:
\r
4308 CopyGameListToClipboard();
\r
4311 /* [AS] Autodetect FEN or PGN data */
\r
4312 case IDM_PasteAny:
\r
4313 PasteGameOrFENFromClipboard();
\r
4316 /* [AS] Move history */
\r
4317 case IDM_ShowMoveHistory:
\r
4318 if( MoveHistoryIsUp() ) {
\r
4319 MoveHistoryPopDown();
\r
4322 MoveHistoryPopUp();
\r
4326 /* [AS] Eval graph */
\r
4327 case IDM_ShowEvalGraph:
\r
4328 if( EvalGraphIsUp() ) {
\r
4329 EvalGraphPopDown();
\r
4333 SetFocus(hwndMain);
\r
4337 /* [AS] Engine output */
\r
4338 case IDM_ShowEngineOutput:
\r
4339 if( EngineOutputIsUp() ) {
\r
4340 EngineOutputPopDown();
\r
4343 EngineOutputPopUp();
\r
4347 /* [AS] User adjudication */
\r
4348 case IDM_UserAdjudication_White:
\r
4349 UserAdjudicationEvent( +1 );
\r
4352 case IDM_UserAdjudication_Black:
\r
4353 UserAdjudicationEvent( -1 );
\r
4356 case IDM_UserAdjudication_Draw:
\r
4357 UserAdjudicationEvent( 0 );
\r
4360 /* [AS] Game list options dialog */
\r
4361 case IDM_GameListOptions:
\r
4362 GameListOptions();
\r
4369 case IDM_CopyPosition:
\r
4370 CopyFENToClipboard();
\r
4373 case IDM_PastePosition:
\r
4374 PasteFENFromClipboard();
\r
4377 case IDM_MailMove:
\r
4381 case IDM_ReloadCMailMsg:
\r
4382 Reset(TRUE, TRUE);
\r
4383 ReloadCmailMsgEvent(FALSE);
\r
4386 case IDM_Minimize:
\r
4387 ShowWindow(hwnd, SW_MINIMIZE);
\r
4394 case IDM_MachineWhite:
\r
4395 MachineWhiteEvent();
\r
4397 * refresh the tags dialog only if it's visible
\r
4399 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4401 tags = PGNTags(&gameInfo);
\r
4402 TagsPopUp(tags, CmailMsg());
\r
4405 SAY("computer starts playing white");
\r
4408 case IDM_MachineBlack:
\r
4409 MachineBlackEvent();
\r
4411 * refresh the tags dialog only if it's visible
\r
4413 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4415 tags = PGNTags(&gameInfo);
\r
4416 TagsPopUp(tags, CmailMsg());
\r
4419 SAY("computer starts playing black");
\r
4422 case IDM_TwoMachines:
\r
4423 TwoMachinesEvent();
\r
4425 * refresh the tags dialog only if it's visible
\r
4427 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4429 tags = PGNTags(&gameInfo);
\r
4430 TagsPopUp(tags, CmailMsg());
\r
4433 SAY("programs start playing each other");
\r
4436 case IDM_AnalysisMode:
\r
4437 if (!first.analysisSupport) {
\r
4438 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4439 DisplayError(buf, 0);
\r
4441 SAY("analyzing current position");
\r
4442 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4443 if (appData.icsActive) {
\r
4444 if (gameMode != IcsObserving) {
\r
4445 sprintf(buf, "You are not observing a game");
\r
4446 DisplayError(buf, 0);
\r
4447 /* secure check */
\r
4448 if (appData.icsEngineAnalyze) {
\r
4449 if (appData.debugMode)
\r
4450 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4451 ExitAnalyzeMode();
\r
4457 /* if enable, user want disable icsEngineAnalyze */
\r
4458 if (appData.icsEngineAnalyze) {
\r
4459 ExitAnalyzeMode();
\r
4463 appData.icsEngineAnalyze = TRUE;
\r
4464 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4467 if (!appData.showThinking) ToggleShowThinking();
\r
4468 AnalyzeModeEvent();
\r
4472 case IDM_AnalyzeFile:
\r
4473 if (!first.analysisSupport) {
\r
4474 char buf[MSG_SIZ];
\r
4475 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4476 DisplayError(buf, 0);
\r
4478 if (!appData.showThinking) ToggleShowThinking();
\r
4479 AnalyzeFileEvent();
\r
4480 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4481 AnalysisPeriodicEvent(1);
\r
4485 case IDM_IcsClient:
\r
4489 case IDM_EditGame:
\r
4494 case IDM_EditPosition:
\r
4495 EditPositionEvent();
\r
4496 SAY("to set up a position type a FEN");
\r
4499 case IDM_Training:
\r
4503 case IDM_ShowGameList:
\r
4504 ShowGameListProc();
\r
4507 case IDM_EditTags:
\r
4511 case IDM_EditComment:
\r
4512 if (commentUp && editComment) {
\r
4515 EditCommentEvent();
\r
4535 case IDM_CallFlag:
\r
4555 case IDM_StopObserving:
\r
4556 StopObservingEvent();
\r
4559 case IDM_StopExamining:
\r
4560 StopExaminingEvent();
\r
4563 case IDM_TypeInMove:
\r
4564 PopUpMoveDialog('\000');
\r
4567 case IDM_TypeInName:
\r
4568 PopUpNameDialog('\000');
\r
4571 case IDM_Backward:
\r
4573 SetFocus(hwndMain);
\r
4580 SetFocus(hwndMain);
\r
4585 SetFocus(hwndMain);
\r
4590 SetFocus(hwndMain);
\r
4597 case IDM_TruncateGame:
\r
4598 TruncateGameEvent();
\r
4605 case IDM_RetractMove:
\r
4606 RetractMoveEvent();
\r
4609 case IDM_FlipView:
\r
4610 flipView = !flipView;
\r
4611 DrawPosition(FALSE, NULL);
\r
4614 case IDM_FlipClock:
\r
4615 flipClock = !flipClock;
\r
4616 DisplayBothClocks();
\r
4617 DrawPosition(FALSE, NULL);
\r
4620 case IDM_MuteSounds:
\r
4621 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4622 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4623 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4626 case IDM_GeneralOptions:
\r
4627 GeneralOptionsPopup(hwnd);
\r
4628 DrawPosition(TRUE, NULL);
\r
4631 case IDM_BoardOptions:
\r
4632 BoardOptionsPopup(hwnd);
\r
4635 case IDM_EnginePlayOptions:
\r
4636 EnginePlayOptionsPopup(hwnd);
\r
4639 case IDM_Engine1Options:
\r
4640 EngineOptionsPopup(hwnd, &first);
\r
4643 case IDM_Engine2Options:
\r
4644 EngineOptionsPopup(hwnd, &second);
\r
4647 case IDM_OptionsUCI:
\r
4648 UciOptionsPopup(hwnd);
\r
4651 case IDM_IcsOptions:
\r
4652 IcsOptionsPopup(hwnd);
\r
4656 FontsOptionsPopup(hwnd);
\r
4660 SoundOptionsPopup(hwnd);
\r
4663 case IDM_CommPort:
\r
4664 CommPortOptionsPopup(hwnd);
\r
4667 case IDM_LoadOptions:
\r
4668 LoadOptionsPopup(hwnd);
\r
4671 case IDM_SaveOptions:
\r
4672 SaveOptionsPopup(hwnd);
\r
4675 case IDM_TimeControl:
\r
4676 TimeControlOptionsPopup(hwnd);
\r
4679 case IDM_SaveSettings:
\r
4680 SaveSettings(settingsFileName);
\r
4683 case IDM_SaveSettingsOnExit:
\r
4684 saveSettingsOnExit = !saveSettingsOnExit;
\r
4685 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4686 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4687 MF_CHECKED : MF_UNCHECKED));
\r
4698 case IDM_AboutGame:
\r
4703 appData.debugMode = !appData.debugMode;
\r
4704 if (appData.debugMode) {
\r
4705 char dir[MSG_SIZ];
\r
4706 GetCurrentDirectory(MSG_SIZ, dir);
\r
4707 SetCurrentDirectory(installDir);
\r
4708 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4709 SetCurrentDirectory(dir);
\r
4710 setbuf(debugFP, NULL);
\r
4717 case IDM_HELPCONTENTS:
\r
4718 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4719 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4720 MessageBox (GetFocus(),
\r
4721 "Unable to activate help",
\r
4722 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4726 case IDM_HELPSEARCH:
\r
4727 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4728 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4729 MessageBox (GetFocus(),
\r
4730 "Unable to activate help",
\r
4731 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4735 case IDM_HELPHELP:
\r
4736 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4737 MessageBox (GetFocus(),
\r
4738 "Unable to activate help",
\r
4739 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4744 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4746 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4747 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4748 FreeProcInstance(lpProc);
\r
4751 case IDM_DirectCommand1:
\r
4752 AskQuestionEvent("Direct Command",
\r
4753 "Send to chess program:", "", "1");
\r
4755 case IDM_DirectCommand2:
\r
4756 AskQuestionEvent("Direct Command",
\r
4757 "Send to second chess program:", "", "2");
\r
4760 case EP_WhitePawn:
\r
4761 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4762 fromX = fromY = -1;
\r
4765 case EP_WhiteKnight:
\r
4766 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4767 fromX = fromY = -1;
\r
4770 case EP_WhiteBishop:
\r
4771 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4772 fromX = fromY = -1;
\r
4775 case EP_WhiteRook:
\r
4776 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4777 fromX = fromY = -1;
\r
4780 case EP_WhiteQueen:
\r
4781 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4782 fromX = fromY = -1;
\r
4785 case EP_WhiteFerz:
\r
4786 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4787 fromX = fromY = -1;
\r
4790 case EP_WhiteWazir:
\r
4791 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4792 fromX = fromY = -1;
\r
4795 case EP_WhiteAlfil:
\r
4796 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4797 fromX = fromY = -1;
\r
4800 case EP_WhiteCannon:
\r
4801 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4802 fromX = fromY = -1;
\r
4805 case EP_WhiteCardinal:
\r
4806 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4807 fromX = fromY = -1;
\r
4810 case EP_WhiteMarshall:
\r
4811 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4812 fromX = fromY = -1;
\r
4815 case EP_WhiteKing:
\r
4816 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4817 fromX = fromY = -1;
\r
4820 case EP_BlackPawn:
\r
4821 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4822 fromX = fromY = -1;
\r
4825 case EP_BlackKnight:
\r
4826 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4827 fromX = fromY = -1;
\r
4830 case EP_BlackBishop:
\r
4831 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4832 fromX = fromY = -1;
\r
4835 case EP_BlackRook:
\r
4836 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4837 fromX = fromY = -1;
\r
4840 case EP_BlackQueen:
\r
4841 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4842 fromX = fromY = -1;
\r
4845 case EP_BlackFerz:
\r
4846 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4847 fromX = fromY = -1;
\r
4850 case EP_BlackWazir:
\r
4851 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4852 fromX = fromY = -1;
\r
4855 case EP_BlackAlfil:
\r
4856 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4857 fromX = fromY = -1;
\r
4860 case EP_BlackCannon:
\r
4861 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4862 fromX = fromY = -1;
\r
4865 case EP_BlackCardinal:
\r
4866 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4867 fromX = fromY = -1;
\r
4870 case EP_BlackMarshall:
\r
4871 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4872 fromX = fromY = -1;
\r
4875 case EP_BlackKing:
\r
4876 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4877 fromX = fromY = -1;
\r
4880 case EP_EmptySquare:
\r
4881 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4882 fromX = fromY = -1;
\r
4885 case EP_ClearBoard:
\r
4886 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4887 fromX = fromY = -1;
\r
4891 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4892 fromX = fromY = -1;
\r
4896 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4897 fromX = fromY = -1;
\r
4901 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4902 fromX = fromY = -1;
\r
4906 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4907 fromX = fromY = -1;
\r
4911 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4912 fromX = fromY = -1;
\r
4916 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4917 fromX = fromY = -1;
\r
4921 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4922 fromX = fromY = -1;
\r
4926 DropMenuEvent(WhiteRook, fromX, fromY);
\r
4927 fromX = fromY = -1;
\r
4931 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
4932 fromX = fromY = -1;
\r
4936 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4942 case CLOCK_TIMER_ID:
\r
4943 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
4944 clockTimerEvent = 0;
\r
4945 DecrementClocks(); /* call into back end */
\r
4947 case LOAD_GAME_TIMER_ID:
\r
4948 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
4949 loadGameTimerEvent = 0;
\r
4950 AutoPlayGameLoop(); /* call into back end */
\r
4952 case ANALYSIS_TIMER_ID:
\r
4953 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
4954 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
4955 AnalysisPeriodicEvent(0);
\r
4957 KillTimer(hwnd, analysisTimerEvent);
\r
4958 analysisTimerEvent = 0;
\r
4961 case DELAYED_TIMER_ID:
\r
4962 KillTimer(hwnd, delayedTimerEvent);
\r
4963 delayedTimerEvent = 0;
\r
4964 delayedTimerCallback();
\r
4969 case WM_USER_Input:
\r
4970 InputEvent(hwnd, message, wParam, lParam);
\r
4973 /* [AS] Also move "attached" child windows */
\r
4974 case WM_WINDOWPOSCHANGING:
\r
4976 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
4977 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
4979 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
4980 /* Window is moving */
\r
4983 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
4984 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
4985 rcMain.right = wpMain.x + wpMain.width;
\r
4986 rcMain.top = wpMain.y;
\r
4987 rcMain.bottom = wpMain.y + wpMain.height;
\r
4989 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
4990 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
4991 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
4992 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
4993 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
4994 wpMain.x = lpwp->x;
\r
4995 wpMain.y = lpwp->y;
\r
5000 /* [AS] Snapping */
\r
5001 case WM_ENTERSIZEMOVE:
\r
5002 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5003 if (hwnd == hwndMain) {
\r
5004 doingSizing = TRUE;
\r
5007 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5011 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5012 if (hwnd == hwndMain) {
\r
5013 lastSizing = wParam;
\r
5018 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5019 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5021 case WM_EXITSIZEMOVE:
\r
5022 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5023 if (hwnd == hwndMain) {
\r
5025 doingSizing = FALSE;
\r
5026 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5027 GetClientRect(hwnd, &client);
\r
5028 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5030 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5032 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5035 case WM_DESTROY: /* message: window being destroyed */
\r
5036 PostQuitMessage(0);
\r
5040 if (hwnd == hwndMain) {
\r
5045 default: /* Passes it on if unprocessed */
\r
5046 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5051 /*---------------------------------------------------------------------------*\
\r
5053 * Misc utility routines
\r
5055 \*---------------------------------------------------------------------------*/
\r
5058 * Decent random number generator, at least not as bad as Windows
\r
5059 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5061 unsigned int randstate;
\r
5066 randstate = randstate * 1664525 + 1013904223;
\r
5067 return (int) randstate & 0x7fffffff;
\r
5071 mysrandom(unsigned int seed)
\r
5078 * returns TRUE if user selects a different color, FALSE otherwise
\r
5082 ChangeColor(HWND hwnd, COLORREF *which)
\r
5084 static BOOL firstTime = TRUE;
\r
5085 static DWORD customColors[16];
\r
5087 COLORREF newcolor;
\r
5092 /* Make initial colors in use available as custom colors */
\r
5093 /* Should we put the compiled-in defaults here instead? */
\r
5095 customColors[i++] = lightSquareColor & 0xffffff;
\r
5096 customColors[i++] = darkSquareColor & 0xffffff;
\r
5097 customColors[i++] = whitePieceColor & 0xffffff;
\r
5098 customColors[i++] = blackPieceColor & 0xffffff;
\r
5099 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5100 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5102 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5103 customColors[i++] = textAttribs[ccl].color;
\r
5105 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5106 firstTime = FALSE;
\r
5109 cc.lStructSize = sizeof(cc);
\r
5110 cc.hwndOwner = hwnd;
\r
5111 cc.hInstance = NULL;
\r
5112 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5113 cc.lpCustColors = (LPDWORD) customColors;
\r
5114 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5116 if (!ChooseColor(&cc)) return FALSE;
\r
5118 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5119 if (newcolor == *which) return FALSE;
\r
5120 *which = newcolor;
\r
5124 InitDrawingColors();
\r
5125 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5130 MyLoadSound(MySound *ms)
\r
5136 if (ms->data) free(ms->data);
\r
5139 switch (ms->name[0]) {
\r
5145 /* System sound from Control Panel. Don't preload here. */
\r
5149 if (ms->name[1] == NULLCHAR) {
\r
5150 /* "!" alone = silence */
\r
5153 /* Builtin wave resource. Error if not found. */
\r
5154 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5155 if (h == NULL) break;
\r
5156 ms->data = (void *)LoadResource(hInst, h);
\r
5157 if (h == NULL) break;
\r
5162 /* .wav file. Error if not found. */
\r
5163 f = fopen(ms->name, "rb");
\r
5164 if (f == NULL) break;
\r
5165 if (fstat(fileno(f), &st) < 0) break;
\r
5166 ms->data = malloc(st.st_size);
\r
5167 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5173 char buf[MSG_SIZ];
\r
5174 sprintf(buf, "Error loading sound %s", ms->name);
\r
5175 DisplayError(buf, GetLastError());
\r
5181 MyPlaySound(MySound *ms)
\r
5183 BOOLEAN ok = FALSE;
\r
5185 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5186 switch (ms->name[0]) {
\r
5188 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5193 /* System sound from Control Panel (deprecated feature).
\r
5194 "$" alone or an unset sound name gets default beep (still in use). */
\r
5195 if (ms->name[1]) {
\r
5196 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5198 if (!ok) ok = MessageBeep(MB_OK);
\r
5201 /* Builtin wave resource, or "!" alone for silence */
\r
5202 if (ms->name[1]) {
\r
5203 if (ms->data == NULL) return FALSE;
\r
5204 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5210 /* .wav file. Error if not found. */
\r
5211 if (ms->data == NULL) return FALSE;
\r
5212 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5215 /* Don't print an error: this can happen innocently if the sound driver
\r
5216 is busy; for instance, if another instance of WinBoard is playing
\r
5217 a sound at about the same time. */
\r
5223 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5226 OPENFILENAME *ofn;
\r
5227 static UINT *number; /* gross that this is static */
\r
5229 switch (message) {
\r
5230 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5231 /* Center the dialog over the application window */
\r
5232 ofn = (OPENFILENAME *) lParam;
\r
5233 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5234 number = (UINT *) ofn->lCustData;
\r
5235 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5239 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5240 return FALSE; /* Allow for further processing */
\r
5243 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5244 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5246 return FALSE; /* Allow for further processing */
\r
5252 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5254 static UINT *number;
\r
5255 OPENFILENAME *ofname;
\r
5258 case WM_INITDIALOG:
\r
5259 ofname = (OPENFILENAME *)lParam;
\r
5260 number = (UINT *)(ofname->lCustData);
\r
5263 ofnot = (OFNOTIFY *)lParam;
\r
5264 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5265 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5274 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5275 char *nameFilt, char *dlgTitle, UINT *number,
\r
5276 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5278 OPENFILENAME openFileName;
\r
5279 char buf1[MSG_SIZ];
\r
5282 if (fileName == NULL) fileName = buf1;
\r
5283 if (defName == NULL) {
\r
5284 strcpy(fileName, "*.");
\r
5285 strcat(fileName, defExt);
\r
5287 strcpy(fileName, defName);
\r
5289 if (fileTitle) strcpy(fileTitle, "");
\r
5290 if (number) *number = 0;
\r
5292 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5293 openFileName.hwndOwner = hwnd;
\r
5294 openFileName.hInstance = (HANDLE) hInst;
\r
5295 openFileName.lpstrFilter = nameFilt;
\r
5296 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5297 openFileName.nMaxCustFilter = 0L;
\r
5298 openFileName.nFilterIndex = 1L;
\r
5299 openFileName.lpstrFile = fileName;
\r
5300 openFileName.nMaxFile = MSG_SIZ;
\r
5301 openFileName.lpstrFileTitle = fileTitle;
\r
5302 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5303 openFileName.lpstrInitialDir = NULL;
\r
5304 openFileName.lpstrTitle = dlgTitle;
\r
5305 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5306 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5307 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5308 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5309 openFileName.nFileOffset = 0;
\r
5310 openFileName.nFileExtension = 0;
\r
5311 openFileName.lpstrDefExt = defExt;
\r
5312 openFileName.lCustData = (LONG) number;
\r
5313 openFileName.lpfnHook = oldDialog ?
\r
5314 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5315 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5317 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5318 GetOpenFileName(&openFileName)) {
\r
5319 /* open the file */
\r
5320 f = fopen(openFileName.lpstrFile, write);
\r
5322 MessageBox(hwnd, "File open failed", NULL,
\r
5323 MB_OK|MB_ICONEXCLAMATION);
\r
5327 int err = CommDlgExtendedError();
\r
5328 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5337 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5339 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5342 * Get the first pop-up menu in the menu template. This is the
\r
5343 * menu that TrackPopupMenu displays.
\r
5345 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5347 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5350 * TrackPopup uses screen coordinates, so convert the
\r
5351 * coordinates of the mouse click to screen coordinates.
\r
5353 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5355 /* Draw and track the floating pop-up menu. */
\r
5356 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5357 pt.x, pt.y, 0, hwnd, NULL);
\r
5359 /* Destroy the menu.*/
\r
5360 DestroyMenu(hmenu);
\r
5365 int sizeX, sizeY, newSizeX, newSizeY;
\r
5367 } ResizeEditPlusButtonsClosure;
\r
5370 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5372 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5376 if (hChild == cl->hText) return TRUE;
\r
5377 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5378 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5379 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5380 ScreenToClient(cl->hDlg, &pt);
\r
5381 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5382 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5386 /* Resize a dialog that has a (rich) edit field filling most of
\r
5387 the top, with a row of buttons below */
\r
5389 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5392 int newTextHeight, newTextWidth;
\r
5393 ResizeEditPlusButtonsClosure cl;
\r
5395 /*if (IsIconic(hDlg)) return;*/
\r
5396 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5398 cl.hdwp = BeginDeferWindowPos(8);
\r
5400 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5401 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5402 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5403 if (newTextHeight < 0) {
\r
5404 newSizeY += -newTextHeight;
\r
5405 newTextHeight = 0;
\r
5407 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5408 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5414 cl.newSizeX = newSizeX;
\r
5415 cl.newSizeY = newSizeY;
\r
5416 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5418 EndDeferWindowPos(cl.hdwp);
\r
5421 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5423 RECT rChild, rParent;
\r
5424 int wChild, hChild, wParent, hParent;
\r
5425 int wScreen, hScreen, xNew, yNew;
\r
5428 /* Get the Height and Width of the child window */
\r
5429 GetWindowRect (hwndChild, &rChild);
\r
5430 wChild = rChild.right - rChild.left;
\r
5431 hChild = rChild.bottom - rChild.top;
\r
5433 /* Get the Height and Width of the parent window */
\r
5434 GetWindowRect (hwndParent, &rParent);
\r
5435 wParent = rParent.right - rParent.left;
\r
5436 hParent = rParent.bottom - rParent.top;
\r
5438 /* Get the display limits */
\r
5439 hdc = GetDC (hwndChild);
\r
5440 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5441 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5442 ReleaseDC(hwndChild, hdc);
\r
5444 /* Calculate new X position, then adjust for screen */
\r
5445 xNew = rParent.left + ((wParent - wChild) /2);
\r
5448 } else if ((xNew+wChild) > wScreen) {
\r
5449 xNew = wScreen - wChild;
\r
5452 /* Calculate new Y position, then adjust for screen */
\r
5454 yNew = rParent.top + ((hParent - hChild) /2);
\r
5457 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5462 } else if ((yNew+hChild) > hScreen) {
\r
5463 yNew = hScreen - hChild;
\r
5466 /* Set it, and return */
\r
5467 return SetWindowPos (hwndChild, NULL,
\r
5468 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5471 /* Center one window over another */
\r
5472 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5474 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5477 /*---------------------------------------------------------------------------*\
\r
5479 * Startup Dialog functions
\r
5481 \*---------------------------------------------------------------------------*/
\r
5483 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5485 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5487 while (*cd != NULL) {
\r
5488 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5494 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5496 char buf1[MAX_ARG_LEN];
\r
5499 if (str[0] == '@') {
\r
5500 FILE* f = fopen(str + 1, "r");
\r
5502 DisplayFatalError(str + 1, errno, 2);
\r
5505 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5507 buf1[len] = NULLCHAR;
\r
5511 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5514 char buf[MSG_SIZ];
\r
5515 char *end = strchr(str, '\n');
\r
5516 if (end == NULL) return;
\r
5517 memcpy(buf, str, end - str);
\r
5518 buf[end - str] = NULLCHAR;
\r
5519 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5525 SetStartupDialogEnables(HWND hDlg)
\r
5527 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5528 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5529 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5530 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5531 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5532 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5533 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5534 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5535 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5536 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5537 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5538 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5539 IsDlgButtonChecked(hDlg, OPT_View));
\r
5543 QuoteForFilename(char *filename)
\r
5545 int dquote, space;
\r
5546 dquote = strchr(filename, '"') != NULL;
\r
5547 space = strchr(filename, ' ') != NULL;
\r
5548 if (dquote || space) {
\r
5560 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5562 char buf[MSG_SIZ];
\r
5565 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5566 q = QuoteForFilename(nthcp);
\r
5567 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5568 if (*nthdir != NULLCHAR) {
\r
5569 q = QuoteForFilename(nthdir);
\r
5570 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5572 if (*nthcp == NULLCHAR) {
\r
5573 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5574 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5575 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5576 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5581 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5583 char buf[MSG_SIZ];
\r
5587 switch (message) {
\r
5588 case WM_INITDIALOG:
\r
5589 /* Center the dialog */
\r
5590 CenterWindow (hDlg, GetDesktopWindow());
\r
5591 /* Initialize the dialog items */
\r
5592 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5593 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5594 firstChessProgramNames);
\r
5595 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5596 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5597 secondChessProgramNames);
\r
5598 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5599 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5600 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5601 if (*appData.icsHelper != NULLCHAR) {
\r
5602 char *q = QuoteForFilename(appData.icsHelper);
\r
5603 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5605 if (*appData.icsHost == NULLCHAR) {
\r
5606 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5607 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5608 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5609 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5610 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5613 if (appData.icsActive) {
\r
5614 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5616 else if (appData.noChessProgram) {
\r
5617 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5620 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5623 SetStartupDialogEnables(hDlg);
\r
5627 switch (LOWORD(wParam)) {
\r
5629 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5630 strcpy(buf, "/fcp=");
\r
5631 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5633 ParseArgs(StringGet, &p);
\r
5634 strcpy(buf, "/scp=");
\r
5635 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5637 ParseArgs(StringGet, &p);
\r
5638 appData.noChessProgram = FALSE;
\r
5639 appData.icsActive = FALSE;
\r
5640 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5641 strcpy(buf, "/ics /icshost=");
\r
5642 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5644 ParseArgs(StringGet, &p);
\r
5645 if (appData.zippyPlay) {
\r
5646 strcpy(buf, "/fcp=");
\r
5647 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5649 ParseArgs(StringGet, &p);
\r
5651 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5652 appData.noChessProgram = TRUE;
\r
5653 appData.icsActive = FALSE;
\r
5655 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5656 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5659 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5660 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5662 ParseArgs(StringGet, &p);
\r
5664 EndDialog(hDlg, TRUE);
\r
5671 case IDM_HELPCONTENTS:
\r
5672 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5673 MessageBox (GetFocus(),
\r
5674 "Unable to activate help",
\r
5675 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5680 SetStartupDialogEnables(hDlg);
\r
5688 /*---------------------------------------------------------------------------*\
\r
5690 * About box dialog functions
\r
5692 \*---------------------------------------------------------------------------*/
\r
5694 /* Process messages for "About" dialog box */
\r
5696 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5698 switch (message) {
\r
5699 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5700 /* Center the dialog over the application window */
\r
5701 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5702 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5706 case WM_COMMAND: /* message: received a command */
\r
5707 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5708 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5709 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5717 /*---------------------------------------------------------------------------*\
\r
5719 * Comment Dialog functions
\r
5721 \*---------------------------------------------------------------------------*/
\r
5724 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5726 static HANDLE hwndText = NULL;
\r
5727 int len, newSizeX, newSizeY, flags;
\r
5728 static int sizeX, sizeY;
\r
5733 switch (message) {
\r
5734 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5735 /* Initialize the dialog items */
\r
5736 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5737 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5738 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5739 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5740 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5741 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5742 SetWindowText(hDlg, commentTitle);
\r
5743 if (editComment) {
\r
5744 SetFocus(hwndText);
\r
5746 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5748 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5749 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5750 MAKELPARAM(FALSE, 0));
\r
5751 /* Size and position the dialog */
\r
5752 if (!commentDialog) {
\r
5753 commentDialog = hDlg;
\r
5754 flags = SWP_NOZORDER;
\r
5755 GetClientRect(hDlg, &rect);
\r
5756 sizeX = rect.right;
\r
5757 sizeY = rect.bottom;
\r
5758 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5759 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5760 WINDOWPLACEMENT wp;
\r
5761 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5762 wp.length = sizeof(WINDOWPLACEMENT);
\r
5764 wp.showCmd = SW_SHOW;
\r
5765 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5766 wp.rcNormalPosition.left = wpComment.x;
\r
5767 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5768 wp.rcNormalPosition.top = wpComment.y;
\r
5769 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5770 SetWindowPlacement(hDlg, &wp);
\r
5772 GetClientRect(hDlg, &rect);
\r
5773 newSizeX = rect.right;
\r
5774 newSizeY = rect.bottom;
\r
5775 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5776 newSizeX, newSizeY);
\r
5783 case WM_COMMAND: /* message: received a command */
\r
5784 switch (LOWORD(wParam)) {
\r
5786 if (editComment) {
\r
5788 /* Read changed options from the dialog box */
\r
5789 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5790 len = GetWindowTextLength(hwndText);
\r
5791 str = (char *) malloc(len + 1);
\r
5792 GetWindowText(hwndText, str, len + 1);
\r
5801 ReplaceComment(commentIndex, str);
\r
5808 case OPT_CancelComment:
\r
5812 case OPT_ClearComment:
\r
5813 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5816 case OPT_EditComment:
\r
5817 EditCommentEvent();
\r
5826 newSizeX = LOWORD(lParam);
\r
5827 newSizeY = HIWORD(lParam);
\r
5828 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5833 case WM_GETMINMAXINFO:
\r
5834 /* Prevent resizing window too small */
\r
5835 mmi = (MINMAXINFO *) lParam;
\r
5836 mmi->ptMinTrackSize.x = 100;
\r
5837 mmi->ptMinTrackSize.y = 100;
\r
5844 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5849 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5851 if (str == NULL) str = "";
\r
5852 p = (char *) malloc(2 * strlen(str) + 2);
\r
5855 if (*str == '\n') *q++ = '\r';
\r
5859 if (commentText != NULL) free(commentText);
\r
5861 commentIndex = index;
\r
5862 commentTitle = title;
\r
5864 editComment = edit;
\r
5866 if (commentDialog) {
\r
5867 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5868 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5870 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5871 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5872 hwndMain, (DLGPROC)lpProc);
\r
5873 FreeProcInstance(lpProc);
\r
5879 /*---------------------------------------------------------------------------*\
\r
5881 * Type-in move dialog functions
\r
5883 \*---------------------------------------------------------------------------*/
\r
5886 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5888 char move[MSG_SIZ];
\r
5890 ChessMove moveType;
\r
5891 int fromX, fromY, toX, toY;
\r
5894 switch (message) {
\r
5895 case WM_INITDIALOG:
\r
5896 move[0] = (char) lParam;
\r
5897 move[1] = NULLCHAR;
\r
5898 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5899 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5900 SetWindowText(hInput, move);
\r
5902 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5906 switch (LOWORD(wParam)) {
\r
5908 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5909 { int n; Board board;
\r
5911 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5912 EditPositionPasteFEN(move);
\r
5913 EndDialog(hDlg, TRUE);
\r
5916 // [HGM] movenum: allow move number to be typed in any mode
\r
5917 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5919 EndDialog(hDlg, TRUE);
\r
5923 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5924 gameMode != Training) {
\r
5925 DisplayMoveError("Displayed move is not current");
\r
5927 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
5928 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5929 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
5930 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
5931 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5932 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
5933 if (gameMode != Training)
\r
5934 forwardMostMove = currentMove;
\r
5935 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
5937 DisplayMoveError("Could not parse move");
\r
5940 EndDialog(hDlg, TRUE);
\r
5943 EndDialog(hDlg, FALSE);
\r
5954 PopUpMoveDialog(char firstchar)
\r
5958 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
5959 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
5960 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
5961 gameMode == EditPosition || gameMode == IcsExamining ||
\r
5962 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
5963 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
5964 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
5965 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
5966 gameMode == Training) {
\r
5967 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
5968 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
5969 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
5970 FreeProcInstance(lpProc);
\r
5974 /*---------------------------------------------------------------------------*\
\r
5976 * Type-in name dialog functions
\r
5978 \*---------------------------------------------------------------------------*/
\r
5981 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5983 char move[MSG_SIZ];
\r
5986 switch (message) {
\r
5987 case WM_INITDIALOG:
\r
5988 move[0] = (char) lParam;
\r
5989 move[1] = NULLCHAR;
\r
5990 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5991 hInput = GetDlgItem(hDlg, OPT_Name);
\r
5992 SetWindowText(hInput, move);
\r
5994 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5998 switch (LOWORD(wParam)) {
\r
6000 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6001 appData.userName = strdup(move);
\r
6004 EndDialog(hDlg, TRUE);
\r
6007 EndDialog(hDlg, FALSE);
\r
6018 PopUpNameDialog(char firstchar)
\r
6022 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6023 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6024 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6025 FreeProcInstance(lpProc);
\r
6028 /*---------------------------------------------------------------------------*\
\r
6032 \*---------------------------------------------------------------------------*/
\r
6034 /* Nonmodal error box */
\r
6035 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6036 WPARAM wParam, LPARAM lParam);
\r
6039 ErrorPopUp(char *title, char *content)
\r
6043 BOOLEAN modal = hwndMain == NULL;
\r
6061 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6062 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6065 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6067 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6068 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6069 hwndMain, (DLGPROC)lpProc);
\r
6070 FreeProcInstance(lpProc);
\r
6077 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6078 if (errorDialog == NULL) return;
\r
6079 DestroyWindow(errorDialog);
\r
6080 errorDialog = NULL;
\r
6081 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6085 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6090 switch (message) {
\r
6091 case WM_INITDIALOG:
\r
6092 GetWindowRect(hDlg, &rChild);
\r
6095 SetWindowPos(hDlg, NULL, rChild.left,
\r
6096 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6097 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6101 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6102 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6103 and it doesn't work when you resize the dialog.
\r
6104 For now, just give it a default position.
\r
6106 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6108 errorDialog = hDlg;
\r
6109 SetWindowText(hDlg, errorTitle);
\r
6110 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6111 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6115 switch (LOWORD(wParam)) {
\r
6118 if (errorDialog == hDlg) errorDialog = NULL;
\r
6119 DestroyWindow(hDlg);
\r
6131 HWND gothicDialog = NULL;
\r
6134 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6138 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6140 switch (message) {
\r
6141 case WM_INITDIALOG:
\r
6142 GetWindowRect(hDlg, &rChild);
\r
6144 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6148 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6149 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6150 and it doesn't work when you resize the dialog.
\r
6151 For now, just give it a default position.
\r
6153 gothicDialog = hDlg;
\r
6154 SetWindowText(hDlg, errorTitle);
\r
6155 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6156 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6160 switch (LOWORD(wParam)) {
\r
6163 if (errorDialog == hDlg) errorDialog = NULL;
\r
6164 DestroyWindow(hDlg);
\r
6176 GothicPopUp(char *title, VariantClass variant)
\r
6179 static char *lastTitle;
\r
6181 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6182 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6184 if(lastTitle != title && gothicDialog != NULL) {
\r
6185 DestroyWindow(gothicDialog);
\r
6186 gothicDialog = NULL;
\r
6188 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6189 title = lastTitle;
\r
6190 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6191 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6192 hwndMain, (DLGPROC)lpProc);
\r
6193 FreeProcInstance(lpProc);
\r
6198 /*---------------------------------------------------------------------------*\
\r
6200 * Ics Interaction console functions
\r
6202 \*---------------------------------------------------------------------------*/
\r
6204 #define HISTORY_SIZE 64
\r
6205 static char *history[HISTORY_SIZE];
\r
6206 int histIn = 0, histP = 0;
\r
6209 SaveInHistory(char *cmd)
\r
6211 if (history[histIn] != NULL) {
\r
6212 free(history[histIn]);
\r
6213 history[histIn] = NULL;
\r
6215 if (*cmd == NULLCHAR) return;
\r
6216 history[histIn] = StrSave(cmd);
\r
6217 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6218 if (history[histIn] != NULL) {
\r
6219 free(history[histIn]);
\r
6220 history[histIn] = NULL;
\r
6226 PrevInHistory(char *cmd)
\r
6229 if (histP == histIn) {
\r
6230 if (history[histIn] != NULL) free(history[histIn]);
\r
6231 history[histIn] = StrSave(cmd);
\r
6233 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6234 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6236 return history[histP];
\r
6242 if (histP == histIn) return NULL;
\r
6243 histP = (histP + 1) % HISTORY_SIZE;
\r
6244 return history[histP];
\r
6248 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6252 hmenu = LoadMenu(hInst, "TextMenu");
\r
6253 h = GetSubMenu(hmenu, 0);
\r
6255 if (strcmp(e->item, "-") == 0) {
\r
6256 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6258 if (e->item[0] == '|') {
\r
6259 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
\r
6260 IDM_CommandX + i, &e->item[1]);
\r
6262 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
\r
6271 WNDPROC consoleTextWindowProc;
\r
6274 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6276 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6277 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6281 SetWindowText(hInput, command);
\r
6283 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6285 sel.cpMin = 999999;
\r
6286 sel.cpMax = 999999;
\r
6287 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6292 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6293 if (sel.cpMin == sel.cpMax) {
\r
6294 /* Expand to surrounding word */
\r
6297 tr.chrg.cpMax = sel.cpMin;
\r
6298 tr.chrg.cpMin = --sel.cpMin;
\r
6299 if (sel.cpMin < 0) break;
\r
6300 tr.lpstrText = name;
\r
6301 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6302 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6306 tr.chrg.cpMin = sel.cpMax;
\r
6307 tr.chrg.cpMax = ++sel.cpMax;
\r
6308 tr.lpstrText = name;
\r
6309 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6310 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6313 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6314 MessageBeep(MB_ICONEXCLAMATION);
\r
6318 tr.lpstrText = name;
\r
6319 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6321 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6322 MessageBeep(MB_ICONEXCLAMATION);
\r
6325 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6328 sprintf(buf, "%s %s", command, name);
\r
6329 SetWindowText(hInput, buf);
\r
6330 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6332 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6333 SetWindowText(hInput, buf);
\r
6334 sel.cpMin = 999999;
\r
6335 sel.cpMax = 999999;
\r
6336 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6342 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6347 switch (message) {
\r
6349 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6352 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6355 sel.cpMin = 999999;
\r
6356 sel.cpMax = 999999;
\r
6357 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6358 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6363 if(wParam != '\022') {
\r
6364 if (wParam == '\t') {
\r
6365 if (GetKeyState(VK_SHIFT) < 0) {
\r
6367 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6368 if (buttonDesc[0].hwnd) {
\r
6369 SetFocus(buttonDesc[0].hwnd);
\r
6371 SetFocus(hwndMain);
\r
6375 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6378 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6379 JAWS_DELETE( SetFocus(hInput); )
\r
6380 SendMessage(hInput, message, wParam, lParam);
\r
6383 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6384 case WM_RBUTTONUP:
\r
6385 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6386 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6387 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6390 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6391 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6392 if (sel.cpMin == sel.cpMax) {
\r
6393 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6394 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6396 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6397 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6399 pt.x = LOWORD(lParam);
\r
6400 pt.y = HIWORD(lParam);
\r
6401 MenuPopup(hwnd, pt, hmenu, -1);
\r
6405 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6407 return SendMessage(hInput, message, wParam, lParam);
\r
6408 case WM_MBUTTONDOWN:
\r
6409 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6410 case WM_RBUTTONDOWN:
\r
6411 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6412 /* Move selection here if it was empty */
\r
6414 pt.x = LOWORD(lParam);
\r
6415 pt.y = HIWORD(lParam);
\r
6416 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6417 if (sel.cpMin == sel.cpMax) {
\r
6418 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6419 sel.cpMax = sel.cpMin;
\r
6420 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6422 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6426 switch (LOWORD(wParam)) {
\r
6427 case IDM_QuickPaste:
\r
6429 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6430 if (sel.cpMin == sel.cpMax) {
\r
6431 MessageBeep(MB_ICONEXCLAMATION);
\r
6434 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6435 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6436 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6441 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6444 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6447 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6451 int i = LOWORD(wParam) - IDM_CommandX;
\r
6452 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6453 icsTextMenuEntry[i].command != NULL) {
\r
6454 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6455 icsTextMenuEntry[i].getname,
\r
6456 icsTextMenuEntry[i].immediate);
\r
6464 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6467 WNDPROC consoleInputWindowProc;
\r
6470 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6472 char buf[MSG_SIZ];
\r
6474 static BOOL sendNextChar = FALSE;
\r
6475 static BOOL quoteNextChar = FALSE;
\r
6476 InputSource *is = consoleInputSource;
\r
6480 switch (message) {
\r
6482 if (!appData.localLineEditing || sendNextChar) {
\r
6483 is->buf[0] = (CHAR) wParam;
\r
6485 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6486 sendNextChar = FALSE;
\r
6489 if (quoteNextChar) {
\r
6490 buf[0] = (char) wParam;
\r
6491 buf[1] = NULLCHAR;
\r
6492 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6493 quoteNextChar = FALSE;
\r
6497 case '\r': /* Enter key */
\r
6498 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6499 if (consoleEcho) SaveInHistory(is->buf);
\r
6500 is->buf[is->count++] = '\n';
\r
6501 is->buf[is->count] = NULLCHAR;
\r
6502 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6503 if (consoleEcho) {
\r
6504 ConsoleOutput(is->buf, is->count, TRUE);
\r
6505 } else if (appData.localLineEditing) {
\r
6506 ConsoleOutput("\n", 1, TRUE);
\r
6509 case '\033': /* Escape key */
\r
6510 SetWindowText(hwnd, "");
\r
6511 cf.cbSize = sizeof(CHARFORMAT);
\r
6512 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6513 if (consoleEcho) {
\r
6514 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6516 cf.crTextColor = COLOR_ECHOOFF;
\r
6518 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6519 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6521 case '\t': /* Tab key */
\r
6522 if (GetKeyState(VK_SHIFT) < 0) {
\r
6524 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6527 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6528 if (buttonDesc[0].hwnd) {
\r
6529 SetFocus(buttonDesc[0].hwnd);
\r
6531 SetFocus(hwndMain);
\r
6535 case '\023': /* Ctrl+S */
\r
6536 sendNextChar = TRUE;
\r
6538 case '\021': /* Ctrl+Q */
\r
6539 quoteNextChar = TRUE;
\r
6549 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6550 p = PrevInHistory(buf);
\r
6552 SetWindowText(hwnd, p);
\r
6553 sel.cpMin = 999999;
\r
6554 sel.cpMax = 999999;
\r
6555 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6560 p = NextInHistory();
\r
6562 SetWindowText(hwnd, p);
\r
6563 sel.cpMin = 999999;
\r
6564 sel.cpMax = 999999;
\r
6565 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6571 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6575 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6579 case WM_MBUTTONDOWN:
\r
6580 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6581 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6583 case WM_RBUTTONUP:
\r
6584 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6585 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6586 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6590 hmenu = LoadMenu(hInst, "InputMenu");
\r
6591 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6592 if (sel.cpMin == sel.cpMax) {
\r
6593 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6594 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6596 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6597 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6599 pt.x = LOWORD(lParam);
\r
6600 pt.y = HIWORD(lParam);
\r
6601 MenuPopup(hwnd, pt, hmenu, -1);
\r
6605 switch (LOWORD(wParam)) {
\r
6607 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6609 case IDM_SelectAll:
\r
6611 sel.cpMax = -1; /*999999?*/
\r
6612 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6615 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6618 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6621 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6626 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6629 #define CO_MAX 100000
\r
6630 #define CO_TRIM 1000
\r
6633 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6635 static SnapData sd;
\r
6636 HWND hText, hInput;
\r
6638 static int sizeX, sizeY;
\r
6639 int newSizeX, newSizeY;
\r
6643 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6644 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6646 switch (message) {
\r
6648 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6650 ENLINK *pLink = (ENLINK*)lParam;
\r
6651 if (pLink->msg == WM_LBUTTONUP)
\r
6655 tr.chrg = pLink->chrg;
\r
6656 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6657 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6658 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6659 free(tr.lpstrText);
\r
6663 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6664 hwndConsole = hDlg;
\r
6666 consoleTextWindowProc = (WNDPROC)
\r
6667 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6668 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6669 consoleInputWindowProc = (WNDPROC)
\r
6670 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6671 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6672 Colorize(ColorNormal, TRUE);
\r
6673 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6674 ChangedConsoleFont();
\r
6675 GetClientRect(hDlg, &rect);
\r
6676 sizeX = rect.right;
\r
6677 sizeY = rect.bottom;
\r
6678 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6679 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6680 WINDOWPLACEMENT wp;
\r
6681 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6682 wp.length = sizeof(WINDOWPLACEMENT);
\r
6684 wp.showCmd = SW_SHOW;
\r
6685 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6686 wp.rcNormalPosition.left = wpConsole.x;
\r
6687 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6688 wp.rcNormalPosition.top = wpConsole.y;
\r
6689 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6690 SetWindowPlacement(hDlg, &wp);
\r
6693 // [HGM] Chessknight's change 2004-07-13
\r
6694 else { /* Determine Defaults */
\r
6695 WINDOWPLACEMENT wp;
\r
6696 wpConsole.x = wpMain.width + 1;
\r
6697 wpConsole.y = wpMain.y;
\r
6698 wpConsole.width = screenWidth - wpMain.width;
\r
6699 wpConsole.height = wpMain.height;
\r
6700 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6701 wp.length = sizeof(WINDOWPLACEMENT);
\r
6703 wp.showCmd = SW_SHOW;
\r
6704 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6705 wp.rcNormalPosition.left = wpConsole.x;
\r
6706 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6707 wp.rcNormalPosition.top = wpConsole.y;
\r
6708 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6709 SetWindowPlacement(hDlg, &wp);
\r
6712 // Allow hText to highlight URLs and send notifications on them
\r
6713 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6714 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6715 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6716 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6730 if (IsIconic(hDlg)) break;
\r
6731 newSizeX = LOWORD(lParam);
\r
6732 newSizeY = HIWORD(lParam);
\r
6733 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6734 RECT rectText, rectInput;
\r
6736 int newTextHeight, newTextWidth;
\r
6737 GetWindowRect(hText, &rectText);
\r
6738 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6739 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6740 if (newTextHeight < 0) {
\r
6741 newSizeY += -newTextHeight;
\r
6742 newTextHeight = 0;
\r
6744 SetWindowPos(hText, NULL, 0, 0,
\r
6745 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6746 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6747 pt.x = rectInput.left;
\r
6748 pt.y = rectInput.top + newSizeY - sizeY;
\r
6749 ScreenToClient(hDlg, &pt);
\r
6750 SetWindowPos(hInput, NULL,
\r
6751 pt.x, pt.y, /* needs client coords */
\r
6752 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6753 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6759 case WM_GETMINMAXINFO:
\r
6760 /* Prevent resizing window too small */
\r
6761 mmi = (MINMAXINFO *) lParam;
\r
6762 mmi->ptMinTrackSize.x = 100;
\r
6763 mmi->ptMinTrackSize.y = 100;
\r
6766 /* [AS] Snapping */
\r
6767 case WM_ENTERSIZEMOVE:
\r
6768 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6771 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6774 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6776 case WM_EXITSIZEMOVE:
\r
6777 UpdateICSWidth(hText);
\r
6778 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6781 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6789 if (hwndConsole) return;
\r
6790 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6791 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6796 ConsoleOutput(char* data, int length, int forceVisible)
\r
6801 char buf[CO_MAX+1];
\r
6804 static int delayLF = 0;
\r
6805 CHARRANGE savesel, sel;
\r
6807 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6815 while (length--) {
\r
6823 } else if (*p == '\007') {
\r
6824 MyPlaySound(&sounds[(int)SoundBell]);
\r
6831 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6832 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6833 /* Save current selection */
\r
6834 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6835 exlen = GetWindowTextLength(hText);
\r
6836 /* Find out whether current end of text is visible */
\r
6837 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6838 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6839 /* Trim existing text if it's too long */
\r
6840 if (exlen + (q - buf) > CO_MAX) {
\r
6841 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6844 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6845 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6847 savesel.cpMin -= trim;
\r
6848 savesel.cpMax -= trim;
\r
6849 if (exlen < 0) exlen = 0;
\r
6850 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6851 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6853 /* Append the new text */
\r
6854 sel.cpMin = exlen;
\r
6855 sel.cpMax = exlen;
\r
6856 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6857 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6858 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6859 if (forceVisible || exlen == 0 ||
\r
6860 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6861 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6862 /* Scroll to make new end of text visible if old end of text
\r
6863 was visible or new text is an echo of user typein */
\r
6864 sel.cpMin = 9999999;
\r
6865 sel.cpMax = 9999999;
\r
6866 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6867 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6868 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6869 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6871 if (savesel.cpMax == exlen || forceVisible) {
\r
6872 /* Move insert point to new end of text if it was at the old
\r
6873 end of text or if the new text is an echo of user typein */
\r
6874 sel.cpMin = 9999999;
\r
6875 sel.cpMax = 9999999;
\r
6876 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6878 /* Restore previous selection */
\r
6879 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6881 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6888 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6892 COLORREF oldFg, oldBg;
\r
6896 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6898 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6899 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6900 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6903 rect.right = x + squareSize;
\r
6905 rect.bottom = y + squareSize;
\r
6908 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6909 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6910 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6911 &rect, str, strlen(str), NULL);
\r
6913 (void) SetTextColor(hdc, oldFg);
\r
6914 (void) SetBkColor(hdc, oldBg);
\r
6915 (void) SelectObject(hdc, oldFont);
\r
6919 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6920 RECT *rect, char *color, char *flagFell)
\r
6924 COLORREF oldFg, oldBg;
\r
6927 if (appData.clockMode) {
\r
6929 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
6931 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
6938 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6939 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6941 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
6942 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
6944 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6948 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6949 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
6950 rect, str, strlen(str), NULL);
\r
6951 if(logoHeight > 0 && appData.clockMode) {
\r
6953 sprintf(buf, "%s %s", buf+7, flagFell);
\r
6954 r.top = rect->top + logoHeight/2;
\r
6955 r.left = rect->left;
\r
6956 r.right = rect->right;
\r
6957 r.bottom = rect->bottom;
\r
6958 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6959 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
6960 &r, str, strlen(str), NULL);
\r
6962 (void) SetTextColor(hdc, oldFg);
\r
6963 (void) SetBkColor(hdc, oldBg);
\r
6964 (void) SelectObject(hdc, oldFont);
\r
6969 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
6975 if( count <= 0 ) {
\r
6976 if (appData.debugMode) {
\r
6977 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
6980 return ERROR_INVALID_USER_BUFFER;
\r
6983 ResetEvent(ovl->hEvent);
\r
6984 ovl->Offset = ovl->OffsetHigh = 0;
\r
6985 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
6989 err = GetLastError();
\r
6990 if (err == ERROR_IO_PENDING) {
\r
6991 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
6995 err = GetLastError();
\r
7002 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7007 ResetEvent(ovl->hEvent);
\r
7008 ovl->Offset = ovl->OffsetHigh = 0;
\r
7009 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7013 err = GetLastError();
\r
7014 if (err == ERROR_IO_PENDING) {
\r
7015 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7019 err = GetLastError();
\r
7025 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7026 void CheckForInputBufferFull( InputSource * is )
\r
7028 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7029 /* Look for end of line */
\r
7030 char * p = is->buf;
\r
7032 while( p < is->next && *p != '\n' ) {
\r
7036 if( p >= is->next ) {
\r
7037 if (appData.debugMode) {
\r
7038 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7041 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7042 is->count = (DWORD) -1;
\r
7043 is->next = is->buf;
\r
7049 InputThread(LPVOID arg)
\r
7054 is = (InputSource *) arg;
\r
7055 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7056 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7057 while (is->hThread != NULL) {
\r
7058 is->error = DoReadFile(is->hFile, is->next,
\r
7059 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7060 &is->count, &ovl);
\r
7061 if (is->error == NO_ERROR) {
\r
7062 is->next += is->count;
\r
7064 if (is->error == ERROR_BROKEN_PIPE) {
\r
7065 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7068 is->count = (DWORD) -1;
\r
7069 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7074 CheckForInputBufferFull( is );
\r
7076 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7078 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7080 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7083 CloseHandle(ovl.hEvent);
\r
7084 CloseHandle(is->hFile);
\r
7086 if (appData.debugMode) {
\r
7087 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7094 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7096 NonOvlInputThread(LPVOID arg)
\r
7103 is = (InputSource *) arg;
\r
7104 while (is->hThread != NULL) {
\r
7105 is->error = ReadFile(is->hFile, is->next,
\r
7106 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7107 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7108 if (is->error == NO_ERROR) {
\r
7109 /* Change CRLF to LF */
\r
7110 if (is->next > is->buf) {
\r
7112 i = is->count + 1;
\r
7120 if (prev == '\r' && *p == '\n') {
\r
7132 if (is->error == ERROR_BROKEN_PIPE) {
\r
7133 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7136 is->count = (DWORD) -1;
\r
7140 CheckForInputBufferFull( is );
\r
7142 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7144 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7146 if (is->count < 0) break; /* Quit on error */
\r
7148 CloseHandle(is->hFile);
\r
7153 SocketInputThread(LPVOID arg)
\r
7157 is = (InputSource *) arg;
\r
7158 while (is->hThread != NULL) {
\r
7159 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7160 if ((int)is->count == SOCKET_ERROR) {
\r
7161 is->count = (DWORD) -1;
\r
7162 is->error = WSAGetLastError();
\r
7164 is->error = NO_ERROR;
\r
7165 is->next += is->count;
\r
7166 if (is->count == 0 && is->second == is) {
\r
7167 /* End of file on stderr; quit with no message */
\r
7171 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7173 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7175 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7181 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7185 is = (InputSource *) lParam;
\r
7186 if (is->lineByLine) {
\r
7187 /* Feed in lines one by one */
\r
7188 char *p = is->buf;
\r
7190 while (q < is->next) {
\r
7191 if (*q++ == '\n') {
\r
7192 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7197 /* Move any partial line to the start of the buffer */
\r
7199 while (p < is->next) {
\r
7204 if (is->error != NO_ERROR || is->count == 0) {
\r
7205 /* Notify backend of the error. Note: If there was a partial
\r
7206 line at the end, it is not flushed through. */
\r
7207 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7210 /* Feed in the whole chunk of input at once */
\r
7211 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7212 is->next = is->buf;
\r
7216 /*---------------------------------------------------------------------------*\
\r
7218 * Menu enables. Used when setting various modes.
\r
7220 \*---------------------------------------------------------------------------*/
\r
7228 GreyRevert(Boolean grey)
\r
7229 { // [HGM] vari: for retracting variations in local mode
\r
7230 HMENU hmenu = GetMenu(hwndMain);
\r
7231 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7235 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7237 while (enab->item > 0) {
\r
7238 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7243 Enables gnuEnables[] = {
\r
7244 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7245 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7246 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7247 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7248 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7249 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7250 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7251 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7252 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7253 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7254 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7258 Enables icsEnables[] = {
\r
7259 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7260 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7261 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7262 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7263 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7264 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7265 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7266 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7267 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7268 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7269 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7270 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7271 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7272 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7273 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7278 Enables zippyEnables[] = {
\r
7279 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7280 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7281 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7282 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7287 Enables ncpEnables[] = {
\r
7288 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7289 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7290 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7291 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7292 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7293 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7294 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7295 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7296 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7297 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7298 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7299 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7300 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7301 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7302 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7303 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7304 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7305 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7306 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7310 Enables trainingOnEnables[] = {
\r
7311 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7312 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7313 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7314 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7315 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7316 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7317 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7318 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7322 Enables trainingOffEnables[] = {
\r
7323 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7324 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7325 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7326 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7327 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7328 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7329 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7330 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7334 /* These modify either ncpEnables or gnuEnables */
\r
7335 Enables cmailEnables[] = {
\r
7336 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7337 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7338 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7339 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7340 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7341 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7342 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7346 Enables machineThinkingEnables[] = {
\r
7347 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7348 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7349 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7350 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7351 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7352 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7353 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7354 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7355 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7356 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7357 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7358 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7359 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7360 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7361 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7365 Enables userThinkingEnables[] = {
\r
7366 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7367 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7368 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7369 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7370 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7371 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7372 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7373 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7374 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7375 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7376 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7377 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7378 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7379 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7380 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7384 /*---------------------------------------------------------------------------*\
\r
7386 * Front-end interface functions exported by XBoard.
\r
7387 * Functions appear in same order as prototypes in frontend.h.
\r
7389 \*---------------------------------------------------------------------------*/
\r
7393 static UINT prevChecked = 0;
\r
7394 static int prevPausing = 0;
\r
7397 if (pausing != prevPausing) {
\r
7398 prevPausing = pausing;
\r
7399 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7400 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7401 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7404 switch (gameMode) {
\r
7405 case BeginningOfGame:
\r
7406 if (appData.icsActive)
\r
7407 nowChecked = IDM_IcsClient;
\r
7408 else if (appData.noChessProgram)
\r
7409 nowChecked = IDM_EditGame;
\r
7411 nowChecked = IDM_MachineBlack;
\r
7413 case MachinePlaysBlack:
\r
7414 nowChecked = IDM_MachineBlack;
\r
7416 case MachinePlaysWhite:
\r
7417 nowChecked = IDM_MachineWhite;
\r
7419 case TwoMachinesPlay:
\r
7420 nowChecked = IDM_TwoMachines;
\r
7423 nowChecked = IDM_AnalysisMode;
\r
7426 nowChecked = IDM_AnalyzeFile;
\r
7429 nowChecked = IDM_EditGame;
\r
7431 case PlayFromGameFile:
\r
7432 nowChecked = IDM_LoadGame;
\r
7434 case EditPosition:
\r
7435 nowChecked = IDM_EditPosition;
\r
7438 nowChecked = IDM_Training;
\r
7440 case IcsPlayingWhite:
\r
7441 case IcsPlayingBlack:
\r
7442 case IcsObserving:
\r
7444 nowChecked = IDM_IcsClient;
\r
7451 if (prevChecked != 0)
\r
7452 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7453 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7454 if (nowChecked != 0)
\r
7455 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7456 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7458 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7459 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7460 MF_BYCOMMAND|MF_ENABLED);
\r
7462 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7463 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7466 prevChecked = nowChecked;
\r
7468 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7469 if (appData.icsActive) {
\r
7470 if (appData.icsEngineAnalyze) {
\r
7471 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7472 MF_BYCOMMAND|MF_CHECKED);
\r
7474 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7475 MF_BYCOMMAND|MF_UNCHECKED);
\r
7483 HMENU hmenu = GetMenu(hwndMain);
\r
7484 SetMenuEnables(hmenu, icsEnables);
\r
7485 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7486 MF_BYPOSITION|MF_ENABLED);
\r
7488 if (appData.zippyPlay) {
\r
7489 SetMenuEnables(hmenu, zippyEnables);
\r
7490 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7491 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7492 MF_BYCOMMAND|MF_ENABLED);
\r
7500 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7506 HMENU hmenu = GetMenu(hwndMain);
\r
7507 SetMenuEnables(hmenu, ncpEnables);
\r
7508 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7509 MF_BYPOSITION|MF_GRAYED);
\r
7510 DrawMenuBar(hwndMain);
\r
7516 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7520 SetTrainingModeOn()
\r
7523 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7524 for (i = 0; i < N_BUTTONS; i++) {
\r
7525 if (buttonDesc[i].hwnd != NULL)
\r
7526 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7531 VOID SetTrainingModeOff()
\r
7534 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7535 for (i = 0; i < N_BUTTONS; i++) {
\r
7536 if (buttonDesc[i].hwnd != NULL)
\r
7537 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7543 SetUserThinkingEnables()
\r
7545 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7549 SetMachineThinkingEnables()
\r
7551 HMENU hMenu = GetMenu(hwndMain);
\r
7552 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7554 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7556 if (gameMode == MachinePlaysBlack) {
\r
7557 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7558 } else if (gameMode == MachinePlaysWhite) {
\r
7559 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7560 } else if (gameMode == TwoMachinesPlay) {
\r
7561 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7567 DisplayTitle(char *str)
\r
7569 char title[MSG_SIZ], *host;
\r
7570 if (str[0] != NULLCHAR) {
\r
7571 strcpy(title, str);
\r
7572 } else if (appData.icsActive) {
\r
7573 if (appData.icsCommPort[0] != NULLCHAR)
\r
7576 host = appData.icsHost;
\r
7577 sprintf(title, "%s: %s", szTitle, host);
\r
7578 } else if (appData.noChessProgram) {
\r
7579 strcpy(title, szTitle);
\r
7581 strcpy(title, szTitle);
\r
7582 strcat(title, ": ");
\r
7583 strcat(title, first.tidy);
\r
7585 SetWindowText(hwndMain, title);
\r
7590 DisplayMessage(char *str1, char *str2)
\r
7594 int remain = MESSAGE_TEXT_MAX - 1;
\r
7597 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7598 messageText[0] = NULLCHAR;
\r
7600 len = strlen(str1);
\r
7601 if (len > remain) len = remain;
\r
7602 strncpy(messageText, str1, len);
\r
7603 messageText[len] = NULLCHAR;
\r
7606 if (*str2 && remain >= 2) {
\r
7608 strcat(messageText, " ");
\r
7611 len = strlen(str2);
\r
7612 if (len > remain) len = remain;
\r
7613 strncat(messageText, str2, len);
\r
7615 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7617 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7621 hdc = GetDC(hwndMain);
\r
7622 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7623 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7624 &messageRect, messageText, strlen(messageText), NULL);
\r
7625 (void) SelectObject(hdc, oldFont);
\r
7626 (void) ReleaseDC(hwndMain, hdc);
\r
7630 DisplayError(char *str, int error)
\r
7632 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7638 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7639 NULL, error, LANG_NEUTRAL,
\r
7640 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7642 sprintf(buf, "%s:\n%s", str, buf2);
\r
7644 ErrorMap *em = errmap;
\r
7645 while (em->err != 0 && em->err != error) em++;
\r
7646 if (em->err != 0) {
\r
7647 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7649 sprintf(buf, "%s:\nError code %d", str, error);
\r
7654 ErrorPopUp("Error", buf);
\r
7659 DisplayMoveError(char *str)
\r
7661 fromX = fromY = -1;
\r
7662 ClearHighlights();
\r
7663 DrawPosition(FALSE, NULL);
\r
7664 if (appData.popupMoveErrors) {
\r
7665 ErrorPopUp("Error", str);
\r
7667 DisplayMessage(str, "");
\r
7668 moveErrorMessageUp = TRUE;
\r
7673 DisplayFatalError(char *str, int error, int exitStatus)
\r
7675 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7677 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7680 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7681 NULL, error, LANG_NEUTRAL,
\r
7682 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7684 sprintf(buf, "%s:\n%s", str, buf2);
\r
7686 ErrorMap *em = errmap;
\r
7687 while (em->err != 0 && em->err != error) em++;
\r
7688 if (em->err != 0) {
\r
7689 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7691 sprintf(buf, "%s:\nError code %d", str, error);
\r
7696 if (appData.debugMode) {
\r
7697 fprintf(debugFP, "%s: %s\n", label, str);
\r
7699 if (appData.popupExitMessage) {
\r
7700 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7701 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7703 ExitEvent(exitStatus);
\r
7708 DisplayInformation(char *str)
\r
7710 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7715 DisplayNote(char *str)
\r
7717 ErrorPopUp("Note", str);
\r
7722 char *title, *question, *replyPrefix;
\r
7727 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7729 static QuestionParams *qp;
\r
7730 char reply[MSG_SIZ];
\r
7733 switch (message) {
\r
7734 case WM_INITDIALOG:
\r
7735 qp = (QuestionParams *) lParam;
\r
7736 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7737 SetWindowText(hDlg, qp->title);
\r
7738 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7739 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7743 switch (LOWORD(wParam)) {
\r
7745 strcpy(reply, qp->replyPrefix);
\r
7746 if (*reply) strcat(reply, " ");
\r
7747 len = strlen(reply);
\r
7748 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7749 strcat(reply, "\n");
\r
7750 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7751 EndDialog(hDlg, TRUE);
\r
7752 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7755 EndDialog(hDlg, FALSE);
\r
7766 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7768 QuestionParams qp;
\r
7772 qp.question = question;
\r
7773 qp.replyPrefix = replyPrefix;
\r
7775 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7776 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7777 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7778 FreeProcInstance(lpProc);
\r
7781 /* [AS] Pick FRC position */
\r
7782 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7784 static int * lpIndexFRC;
\r
7790 case WM_INITDIALOG:
\r
7791 lpIndexFRC = (int *) lParam;
\r
7793 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7795 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7796 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7797 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7798 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7803 switch( LOWORD(wParam) ) {
\r
7805 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7806 EndDialog( hDlg, 0 );
\r
7807 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7810 EndDialog( hDlg, 1 );
\r
7812 case IDC_NFG_Edit:
\r
7813 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7814 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7816 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7819 case IDC_NFG_Random:
\r
7820 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7821 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7834 int index = appData.defaultFrcPosition;
\r
7835 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7837 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7839 if( result == 0 ) {
\r
7840 appData.defaultFrcPosition = index;
\r
7846 /* [AS] Game list options. Refactored by HGM */
\r
7848 HWND gameListOptionsDialog;
\r
7850 // low-level front-end: clear text edit / list widget
\r
7854 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7857 // low-level front-end: clear text edit / list widget
\r
7859 GLT_DeSelectList()
\r
7861 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7864 // low-level front-end: append line to text edit / list widget
\r
7866 GLT_AddToList( char *name )
\r
7869 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
7873 // low-level front-end: get line from text edit / list widget
\r
7875 GLT_GetFromList( int index, char *name )
\r
7878 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
7884 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7886 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7887 int idx2 = idx1 + delta;
\r
7888 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7890 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7893 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7894 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7895 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7896 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7900 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7904 case WM_INITDIALOG:
\r
7905 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
7907 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7909 /* Initialize list */
\r
7910 GLT_TagsToList( lpUserGLT );
\r
7912 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
7917 switch( LOWORD(wParam) ) {
\r
7920 EndDialog( hDlg, 0 );
\r
7923 EndDialog( hDlg, 1 );
\r
7926 case IDC_GLT_Default:
\r
7927 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
7930 case IDC_GLT_Restore:
\r
7931 GLT_TagsToList( appData.gameListTags );
\r
7935 GLT_MoveSelection( hDlg, -1 );
\r
7938 case IDC_GLT_Down:
\r
7939 GLT_MoveSelection( hDlg, +1 );
\r
7949 int GameListOptions()
\r
7952 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
7954 strcpy( lpUserGLT, appData.gameListTags );
\r
7956 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
7958 if( result == 0 ) {
\r
7959 /* [AS] Memory leak here! */
\r
7960 appData.gameListTags = strdup( lpUserGLT );
\r
7967 DisplayIcsInteractionTitle(char *str)
\r
7969 char consoleTitle[MSG_SIZ];
\r
7971 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
7972 SetWindowText(hwndConsole, consoleTitle);
\r
7976 DrawPosition(int fullRedraw, Board board)
\r
7978 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
7981 void NotifyFrontendLogin()
\r
7984 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
7990 fromX = fromY = -1;
\r
7991 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
7992 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
7993 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
7994 dragInfo.lastpos = dragInfo.pos;
\r
7995 dragInfo.start.x = dragInfo.start.y = -1;
\r
7996 dragInfo.from = dragInfo.start;
\r
7998 DrawPosition(TRUE, NULL);
\r
8004 CommentPopUp(char *title, char *str)
\r
8006 HWND hwnd = GetActiveWindow();
\r
8007 EitherCommentPopUp(0, title, str, FALSE);
\r
8009 SetActiveWindow(hwnd);
\r
8013 CommentPopDown(void)
\r
8015 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8016 if (commentDialog) {
\r
8017 ShowWindow(commentDialog, SW_HIDE);
\r
8019 commentUp = FALSE;
\r
8023 EditCommentPopUp(int index, char *title, char *str)
\r
8025 EitherCommentPopUp(index, title, str, TRUE);
\r
8032 MyPlaySound(&sounds[(int)SoundMove]);
\r
8035 VOID PlayIcsWinSound()
\r
8037 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8040 VOID PlayIcsLossSound()
\r
8042 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8045 VOID PlayIcsDrawSound()
\r
8047 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8050 VOID PlayIcsUnfinishedSound()
\r
8052 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8058 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8066 consoleEcho = TRUE;
\r
8067 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8068 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8069 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8078 consoleEcho = FALSE;
\r
8079 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8080 /* This works OK: set text and background both to the same color */
\r
8082 cf.crTextColor = COLOR_ECHOOFF;
\r
8083 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8084 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8087 /* No Raw()...? */
\r
8089 void Colorize(ColorClass cc, int continuation)
\r
8091 currentColorClass = cc;
\r
8092 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8093 consoleCF.crTextColor = textAttribs[cc].color;
\r
8094 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8095 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8101 static char buf[MSG_SIZ];
\r
8102 DWORD bufsiz = MSG_SIZ;
\r
8104 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8105 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8107 if (!GetUserName(buf, &bufsiz)) {
\r
8108 /*DisplayError("Error getting user name", GetLastError());*/
\r
8109 strcpy(buf, "User");
\r
8117 static char buf[MSG_SIZ];
\r
8118 DWORD bufsiz = MSG_SIZ;
\r
8120 if (!GetComputerName(buf, &bufsiz)) {
\r
8121 /*DisplayError("Error getting host name", GetLastError());*/
\r
8122 strcpy(buf, "Unknown");
\r
8129 ClockTimerRunning()
\r
8131 return clockTimerEvent != 0;
\r
8137 if (clockTimerEvent == 0) return FALSE;
\r
8138 KillTimer(hwndMain, clockTimerEvent);
\r
8139 clockTimerEvent = 0;
\r
8144 StartClockTimer(long millisec)
\r
8146 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8147 (UINT) millisec, NULL);
\r
8151 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8154 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8156 if(appData.noGUI) return;
\r
8157 hdc = GetDC(hwndMain);
\r
8158 if (!IsIconic(hwndMain)) {
\r
8159 DisplayAClock(hdc, timeRemaining, highlight,
\r
8160 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8162 if (highlight && iconCurrent == iconBlack) {
\r
8163 iconCurrent = iconWhite;
\r
8164 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8165 if (IsIconic(hwndMain)) {
\r
8166 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8169 (void) ReleaseDC(hwndMain, hdc);
\r
8171 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8175 DisplayBlackClock(long timeRemaining, int highlight)
\r
8178 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8180 if(appData.noGUI) return;
\r
8181 hdc = GetDC(hwndMain);
\r
8182 if (!IsIconic(hwndMain)) {
\r
8183 DisplayAClock(hdc, timeRemaining, highlight,
\r
8184 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8186 if (highlight && iconCurrent == iconWhite) {
\r
8187 iconCurrent = iconBlack;
\r
8188 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8189 if (IsIconic(hwndMain)) {
\r
8190 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8193 (void) ReleaseDC(hwndMain, hdc);
\r
8195 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8200 LoadGameTimerRunning()
\r
8202 return loadGameTimerEvent != 0;
\r
8206 StopLoadGameTimer()
\r
8208 if (loadGameTimerEvent == 0) return FALSE;
\r
8209 KillTimer(hwndMain, loadGameTimerEvent);
\r
8210 loadGameTimerEvent = 0;
\r
8215 StartLoadGameTimer(long millisec)
\r
8217 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8218 (UINT) millisec, NULL);
\r
8226 char fileTitle[MSG_SIZ];
\r
8228 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8229 f = OpenFileDialog(hwndMain, "a", defName,
\r
8230 appData.oldSaveStyle ? "gam" : "pgn",
\r
8232 "Save Game to File", NULL, fileTitle, NULL);
\r
8234 SaveGame(f, 0, "");
\r
8241 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8243 if (delayedTimerEvent != 0) {
\r
8244 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8245 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8247 KillTimer(hwndMain, delayedTimerEvent);
\r
8248 delayedTimerEvent = 0;
\r
8249 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8250 delayedTimerCallback();
\r
8252 delayedTimerCallback = cb;
\r
8253 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8254 (UINT) millisec, NULL);
\r
8257 DelayedEventCallback
\r
8260 if (delayedTimerEvent) {
\r
8261 return delayedTimerCallback;
\r
8268 CancelDelayedEvent()
\r
8270 if (delayedTimerEvent) {
\r
8271 KillTimer(hwndMain, delayedTimerEvent);
\r
8272 delayedTimerEvent = 0;
\r
8276 DWORD GetWin32Priority(int nice)
\r
8277 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8279 REALTIME_PRIORITY_CLASS 0x00000100
\r
8280 HIGH_PRIORITY_CLASS 0x00000080
\r
8281 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8282 NORMAL_PRIORITY_CLASS 0x00000020
\r
8283 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8284 IDLE_PRIORITY_CLASS 0x00000040
\r
8286 if (nice < -15) return 0x00000080;
\r
8287 if (nice < 0) return 0x00008000;
\r
8288 if (nice == 0) return 0x00000020;
\r
8289 if (nice < 15) return 0x00004000;
\r
8290 return 0x00000040;
\r
8293 /* Start a child process running the given program.
\r
8294 The process's standard output can be read from "from", and its
\r
8295 standard input can be written to "to".
\r
8296 Exit with fatal error if anything goes wrong.
\r
8297 Returns an opaque pointer that can be used to destroy the process
\r
8301 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8303 #define BUFSIZE 4096
\r
8305 HANDLE hChildStdinRd, hChildStdinWr,
\r
8306 hChildStdoutRd, hChildStdoutWr;
\r
8307 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8308 SECURITY_ATTRIBUTES saAttr;
\r
8310 PROCESS_INFORMATION piProcInfo;
\r
8311 STARTUPINFO siStartInfo;
\r
8313 char buf[MSG_SIZ];
\r
8316 if (appData.debugMode) {
\r
8317 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8322 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8323 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8324 saAttr.bInheritHandle = TRUE;
\r
8325 saAttr.lpSecurityDescriptor = NULL;
\r
8328 * The steps for redirecting child's STDOUT:
\r
8329 * 1. Create anonymous pipe to be STDOUT for child.
\r
8330 * 2. Create a noninheritable duplicate of read handle,
\r
8331 * and close the inheritable read handle.
\r
8334 /* Create a pipe for the child's STDOUT. */
\r
8335 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8336 return GetLastError();
\r
8339 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8340 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8341 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8342 FALSE, /* not inherited */
\r
8343 DUPLICATE_SAME_ACCESS);
\r
8345 return GetLastError();
\r
8347 CloseHandle(hChildStdoutRd);
\r
8350 * The steps for redirecting child's STDIN:
\r
8351 * 1. Create anonymous pipe to be STDIN for child.
\r
8352 * 2. Create a noninheritable duplicate of write handle,
\r
8353 * and close the inheritable write handle.
\r
8356 /* Create a pipe for the child's STDIN. */
\r
8357 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8358 return GetLastError();
\r
8361 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8362 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8363 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8364 FALSE, /* not inherited */
\r
8365 DUPLICATE_SAME_ACCESS);
\r
8367 return GetLastError();
\r
8369 CloseHandle(hChildStdinWr);
\r
8371 /* Arrange to (1) look in dir for the child .exe file, and
\r
8372 * (2) have dir be the child's working directory. Interpret
\r
8373 * dir relative to the directory WinBoard loaded from. */
\r
8374 GetCurrentDirectory(MSG_SIZ, buf);
\r
8375 SetCurrentDirectory(installDir);
\r
8376 SetCurrentDirectory(dir);
\r
8378 /* Now create the child process. */
\r
8380 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8381 siStartInfo.lpReserved = NULL;
\r
8382 siStartInfo.lpDesktop = NULL;
\r
8383 siStartInfo.lpTitle = NULL;
\r
8384 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8385 siStartInfo.cbReserved2 = 0;
\r
8386 siStartInfo.lpReserved2 = NULL;
\r
8387 siStartInfo.hStdInput = hChildStdinRd;
\r
8388 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8389 siStartInfo.hStdError = hChildStdoutWr;
\r
8391 fSuccess = CreateProcess(NULL,
\r
8392 cmdLine, /* command line */
\r
8393 NULL, /* process security attributes */
\r
8394 NULL, /* primary thread security attrs */
\r
8395 TRUE, /* handles are inherited */
\r
8396 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8397 NULL, /* use parent's environment */
\r
8399 &siStartInfo, /* STARTUPINFO pointer */
\r
8400 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8402 err = GetLastError();
\r
8403 SetCurrentDirectory(buf); /* return to prev directory */
\r
8408 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8409 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8410 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8413 /* Close the handles we don't need in the parent */
\r
8414 CloseHandle(piProcInfo.hThread);
\r
8415 CloseHandle(hChildStdinRd);
\r
8416 CloseHandle(hChildStdoutWr);
\r
8418 /* Prepare return value */
\r
8419 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8420 cp->kind = CPReal;
\r
8421 cp->hProcess = piProcInfo.hProcess;
\r
8422 cp->pid = piProcInfo.dwProcessId;
\r
8423 cp->hFrom = hChildStdoutRdDup;
\r
8424 cp->hTo = hChildStdinWrDup;
\r
8426 *pr = (void *) cp;
\r
8428 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8429 2000 where engines sometimes don't see the initial command(s)
\r
8430 from WinBoard and hang. I don't understand how that can happen,
\r
8431 but the Sleep is harmless, so I've put it in. Others have also
\r
8432 reported what may be the same problem, so hopefully this will fix
\r
8433 it for them too. */
\r
8441 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8443 ChildProc *cp; int result;
\r
8445 cp = (ChildProc *) pr;
\r
8446 if (cp == NULL) return;
\r
8448 switch (cp->kind) {
\r
8450 /* TerminateProcess is considered harmful, so... */
\r
8451 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8452 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8453 /* The following doesn't work because the chess program
\r
8454 doesn't "have the same console" as WinBoard. Maybe
\r
8455 we could arrange for this even though neither WinBoard
\r
8456 nor the chess program uses a console for stdio? */
\r
8457 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8459 /* [AS] Special termination modes for misbehaving programs... */
\r
8460 if( signal == 9 ) {
\r
8461 result = TerminateProcess( cp->hProcess, 0 );
\r
8463 if ( appData.debugMode) {
\r
8464 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8467 else if( signal == 10 ) {
\r
8468 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8470 if( dw != WAIT_OBJECT_0 ) {
\r
8471 result = TerminateProcess( cp->hProcess, 0 );
\r
8473 if ( appData.debugMode) {
\r
8474 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8480 CloseHandle(cp->hProcess);
\r
8484 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8488 closesocket(cp->sock);
\r
8493 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8494 closesocket(cp->sock);
\r
8495 closesocket(cp->sock2);
\r
8503 InterruptChildProcess(ProcRef pr)
\r
8507 cp = (ChildProc *) pr;
\r
8508 if (cp == NULL) return;
\r
8509 switch (cp->kind) {
\r
8511 /* The following doesn't work because the chess program
\r
8512 doesn't "have the same console" as WinBoard. Maybe
\r
8513 we could arrange for this even though neither WinBoard
\r
8514 nor the chess program uses a console for stdio */
\r
8515 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8520 /* Can't interrupt */
\r
8524 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8531 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8533 char cmdLine[MSG_SIZ];
\r
8535 if (port[0] == NULLCHAR) {
\r
8536 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8538 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8540 return StartChildProcess(cmdLine, "", pr);
\r
8544 /* Code to open TCP sockets */
\r
8547 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8552 struct sockaddr_in sa, mysa;
\r
8553 struct hostent FAR *hp;
\r
8554 unsigned short uport;
\r
8555 WORD wVersionRequested;
\r
8558 /* Initialize socket DLL */
\r
8559 wVersionRequested = MAKEWORD(1, 1);
\r
8560 err = WSAStartup(wVersionRequested, &wsaData);
\r
8561 if (err != 0) return err;
\r
8564 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8565 err = WSAGetLastError();
\r
8570 /* Bind local address using (mostly) don't-care values.
\r
8572 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8573 mysa.sin_family = AF_INET;
\r
8574 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8575 uport = (unsigned short) 0;
\r
8576 mysa.sin_port = htons(uport);
\r
8577 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8578 == SOCKET_ERROR) {
\r
8579 err = WSAGetLastError();
\r
8584 /* Resolve remote host name */
\r
8585 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8586 if (!(hp = gethostbyname(host))) {
\r
8587 unsigned int b0, b1, b2, b3;
\r
8589 err = WSAGetLastError();
\r
8591 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8592 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8593 hp->h_addrtype = AF_INET;
\r
8595 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8596 hp->h_addr_list[0] = (char *) malloc(4);
\r
8597 hp->h_addr_list[0][0] = (char) b0;
\r
8598 hp->h_addr_list[0][1] = (char) b1;
\r
8599 hp->h_addr_list[0][2] = (char) b2;
\r
8600 hp->h_addr_list[0][3] = (char) b3;
\r
8606 sa.sin_family = hp->h_addrtype;
\r
8607 uport = (unsigned short) atoi(port);
\r
8608 sa.sin_port = htons(uport);
\r
8609 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8611 /* Make connection */
\r
8612 if (connect(s, (struct sockaddr *) &sa,
\r
8613 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8614 err = WSAGetLastError();
\r
8619 /* Prepare return value */
\r
8620 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8621 cp->kind = CPSock;
\r
8623 *pr = (ProcRef *) cp;
\r
8629 OpenCommPort(char *name, ProcRef *pr)
\r
8634 char fullname[MSG_SIZ];
\r
8636 if (*name != '\\')
\r
8637 sprintf(fullname, "\\\\.\\%s", name);
\r
8639 strcpy(fullname, name);
\r
8641 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8642 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8643 if (h == (HANDLE) -1) {
\r
8644 return GetLastError();
\r
8648 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8650 /* Accumulate characters until a 100ms pause, then parse */
\r
8651 ct.ReadIntervalTimeout = 100;
\r
8652 ct.ReadTotalTimeoutMultiplier = 0;
\r
8653 ct.ReadTotalTimeoutConstant = 0;
\r
8654 ct.WriteTotalTimeoutMultiplier = 0;
\r
8655 ct.WriteTotalTimeoutConstant = 0;
\r
8656 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8658 /* Prepare return value */
\r
8659 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8660 cp->kind = CPComm;
\r
8663 *pr = (ProcRef *) cp;
\r
8669 OpenLoopback(ProcRef *pr)
\r
8671 DisplayFatalError("Not implemented", 0, 1);
\r
8677 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8682 struct sockaddr_in sa, mysa;
\r
8683 struct hostent FAR *hp;
\r
8684 unsigned short uport;
\r
8685 WORD wVersionRequested;
\r
8688 char stderrPortStr[MSG_SIZ];
\r
8690 /* Initialize socket DLL */
\r
8691 wVersionRequested = MAKEWORD(1, 1);
\r
8692 err = WSAStartup(wVersionRequested, &wsaData);
\r
8693 if (err != 0) return err;
\r
8695 /* Resolve remote host name */
\r
8696 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8697 if (!(hp = gethostbyname(host))) {
\r
8698 unsigned int b0, b1, b2, b3;
\r
8700 err = WSAGetLastError();
\r
8702 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8703 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8704 hp->h_addrtype = AF_INET;
\r
8706 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8707 hp->h_addr_list[0] = (char *) malloc(4);
\r
8708 hp->h_addr_list[0][0] = (char) b0;
\r
8709 hp->h_addr_list[0][1] = (char) b1;
\r
8710 hp->h_addr_list[0][2] = (char) b2;
\r
8711 hp->h_addr_list[0][3] = (char) b3;
\r
8717 sa.sin_family = hp->h_addrtype;
\r
8718 uport = (unsigned short) 514;
\r
8719 sa.sin_port = htons(uport);
\r
8720 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8722 /* Bind local socket to unused "privileged" port address
\r
8724 s = INVALID_SOCKET;
\r
8725 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8726 mysa.sin_family = AF_INET;
\r
8727 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8728 for (fromPort = 1023;; fromPort--) {
\r
8729 if (fromPort < 0) {
\r
8731 return WSAEADDRINUSE;
\r
8733 if (s == INVALID_SOCKET) {
\r
8734 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8735 err = WSAGetLastError();
\r
8740 uport = (unsigned short) fromPort;
\r
8741 mysa.sin_port = htons(uport);
\r
8742 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8743 == SOCKET_ERROR) {
\r
8744 err = WSAGetLastError();
\r
8745 if (err == WSAEADDRINUSE) continue;
\r
8749 if (connect(s, (struct sockaddr *) &sa,
\r
8750 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8751 err = WSAGetLastError();
\r
8752 if (err == WSAEADDRINUSE) {
\r
8763 /* Bind stderr local socket to unused "privileged" port address
\r
8765 s2 = INVALID_SOCKET;
\r
8766 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8767 mysa.sin_family = AF_INET;
\r
8768 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8769 for (fromPort = 1023;; fromPort--) {
\r
8770 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8771 if (fromPort < 0) {
\r
8772 (void) closesocket(s);
\r
8774 return WSAEADDRINUSE;
\r
8776 if (s2 == INVALID_SOCKET) {
\r
8777 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8778 err = WSAGetLastError();
\r
8784 uport = (unsigned short) fromPort;
\r
8785 mysa.sin_port = htons(uport);
\r
8786 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8787 == SOCKET_ERROR) {
\r
8788 err = WSAGetLastError();
\r
8789 if (err == WSAEADDRINUSE) continue;
\r
8790 (void) closesocket(s);
\r
8794 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8795 err = WSAGetLastError();
\r
8796 if (err == WSAEADDRINUSE) {
\r
8798 s2 = INVALID_SOCKET;
\r
8801 (void) closesocket(s);
\r
8802 (void) closesocket(s2);
\r
8808 prevStderrPort = fromPort; // remember port used
\r
8809 sprintf(stderrPortStr, "%d", fromPort);
\r
8811 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8812 err = WSAGetLastError();
\r
8813 (void) closesocket(s);
\r
8814 (void) closesocket(s2);
\r
8819 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8820 err = WSAGetLastError();
\r
8821 (void) closesocket(s);
\r
8822 (void) closesocket(s2);
\r
8826 if (*user == NULLCHAR) user = UserName();
\r
8827 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8828 err = WSAGetLastError();
\r
8829 (void) closesocket(s);
\r
8830 (void) closesocket(s2);
\r
8834 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8835 err = WSAGetLastError();
\r
8836 (void) closesocket(s);
\r
8837 (void) closesocket(s2);
\r
8842 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8843 err = WSAGetLastError();
\r
8844 (void) closesocket(s);
\r
8845 (void) closesocket(s2);
\r
8849 (void) closesocket(s2); /* Stop listening */
\r
8851 /* Prepare return value */
\r
8852 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8853 cp->kind = CPRcmd;
\r
8856 *pr = (ProcRef *) cp;
\r
8863 AddInputSource(ProcRef pr, int lineByLine,
\r
8864 InputCallback func, VOIDSTAR closure)
\r
8866 InputSource *is, *is2 = NULL;
\r
8867 ChildProc *cp = (ChildProc *) pr;
\r
8869 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8870 is->lineByLine = lineByLine;
\r
8872 is->closure = closure;
\r
8873 is->second = NULL;
\r
8874 is->next = is->buf;
\r
8875 if (pr == NoProc) {
\r
8876 is->kind = CPReal;
\r
8877 consoleInputSource = is;
\r
8879 is->kind = cp->kind;
\r
8881 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8882 we create all threads suspended so that the is->hThread variable can be
\r
8883 safely assigned, then let the threads start with ResumeThread.
\r
8885 switch (cp->kind) {
\r
8887 is->hFile = cp->hFrom;
\r
8888 cp->hFrom = NULL; /* now owned by InputThread */
\r
8890 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8891 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8895 is->hFile = cp->hFrom;
\r
8896 cp->hFrom = NULL; /* now owned by InputThread */
\r
8898 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
8899 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8903 is->sock = cp->sock;
\r
8905 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8906 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8910 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
8912 is->sock = cp->sock;
\r
8914 is2->sock = cp->sock2;
\r
8915 is2->second = is2;
\r
8917 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8918 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8920 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8921 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
8925 if( is->hThread != NULL ) {
\r
8926 ResumeThread( is->hThread );
\r
8929 if( is2 != NULL && is2->hThread != NULL ) {
\r
8930 ResumeThread( is2->hThread );
\r
8934 return (InputSourceRef) is;
\r
8938 RemoveInputSource(InputSourceRef isr)
\r
8942 is = (InputSource *) isr;
\r
8943 is->hThread = NULL; /* tell thread to stop */
\r
8944 CloseHandle(is->hThread);
\r
8945 if (is->second != NULL) {
\r
8946 is->second->hThread = NULL;
\r
8947 CloseHandle(is->second->hThread);
\r
8951 int no_wrap(char *message, int count)
\r
8953 ConsoleOutput(message, count, FALSE);
\r
8958 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
8961 int outCount = SOCKET_ERROR;
\r
8962 ChildProc *cp = (ChildProc *) pr;
\r
8963 static OVERLAPPED ovl;
\r
8964 static int line = 0;
\r
8968 if (appData.noJoin || !appData.useInternalWrap)
\r
8969 return no_wrap(message, count);
\r
8972 int width = get_term_width();
\r
8973 int len = wrap(NULL, message, count, width, &line);
\r
8974 char *msg = malloc(len);
\r
8978 return no_wrap(message, count);
\r
8981 dbgchk = wrap(msg, message, count, width, &line);
\r
8982 if (dbgchk != len && appData.debugMode)
\r
8983 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
8984 ConsoleOutput(msg, len, FALSE);
\r
8991 if (ovl.hEvent == NULL) {
\r
8992 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
8994 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
8996 switch (cp->kind) {
\r
8999 outCount = send(cp->sock, message, count, 0);
\r
9000 if (outCount == SOCKET_ERROR) {
\r
9001 *outError = WSAGetLastError();
\r
9003 *outError = NO_ERROR;
\r
9008 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9009 &dOutCount, NULL)) {
\r
9010 *outError = NO_ERROR;
\r
9011 outCount = (int) dOutCount;
\r
9013 *outError = GetLastError();
\r
9018 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9019 &dOutCount, &ovl);
\r
9020 if (*outError == NO_ERROR) {
\r
9021 outCount = (int) dOutCount;
\r
9029 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9032 /* Ignore delay, not implemented for WinBoard */
\r
9033 return OutputToProcess(pr, message, count, outError);
\r
9038 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9039 char *buf, int count, int error)
\r
9041 DisplayFatalError("Not implemented", 0, 1);
\r
9044 /* see wgamelist.c for Game List functions */
\r
9045 /* see wedittags.c for Edit Tags functions */
\r
9052 char buf[MSG_SIZ];
\r
9055 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9056 f = fopen(buf, "r");
\r
9058 ProcessICSInitScript(f);
\r
9066 StartAnalysisClock()
\r
9068 if (analysisTimerEvent) return;
\r
9069 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9070 (UINT) 2000, NULL);
\r
9074 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9076 highlightInfo.sq[0].x = fromX;
\r
9077 highlightInfo.sq[0].y = fromY;
\r
9078 highlightInfo.sq[1].x = toX;
\r
9079 highlightInfo.sq[1].y = toY;
\r
9085 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9086 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9090 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9092 premoveHighlightInfo.sq[0].x = fromX;
\r
9093 premoveHighlightInfo.sq[0].y = fromY;
\r
9094 premoveHighlightInfo.sq[1].x = toX;
\r
9095 premoveHighlightInfo.sq[1].y = toY;
\r
9099 ClearPremoveHighlights()
\r
9101 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9102 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9106 ShutDownFrontEnd()
\r
9108 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9109 DeleteClipboardTempFiles();
\r
9115 if (IsIconic(hwndMain))
\r
9116 ShowWindow(hwndMain, SW_RESTORE);
\r
9118 SetActiveWindow(hwndMain);
\r
9122 * Prototypes for animation support routines
\r
9124 static void ScreenSquare(int column, int row, POINT * pt);
\r
9125 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9126 POINT frames[], int * nFrames);
\r
9130 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9131 { // [HGM] atomic: animate blast wave
\r
9133 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9134 explodeInfo.fromX = fromX;
\r
9135 explodeInfo.fromY = fromY;
\r
9136 explodeInfo.toX = toX;
\r
9137 explodeInfo.toY = toY;
\r
9138 for(i=1; i<nFrames; i++) {
\r
9139 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9140 DrawPosition(FALSE, NULL);
\r
9141 Sleep(appData.animSpeed);
\r
9143 explodeInfo.radius = 0;
\r
9144 DrawPosition(TRUE, NULL);
\r
9150 AnimateMove(board, fromX, fromY, toX, toY)
\r
9157 ChessSquare piece;
\r
9158 POINT start, finish, mid;
\r
9159 POINT frames[kFactor * 2 + 1];
\r
9162 if (!appData.animate) return;
\r
9163 if (doingSizing) return;
\r
9164 if (fromY < 0 || fromX < 0) return;
\r
9165 piece = board[fromY][fromX];
\r
9166 if (piece >= EmptySquare) return;
\r
9168 ScreenSquare(fromX, fromY, &start);
\r
9169 ScreenSquare(toX, toY, &finish);
\r
9171 /* All pieces except knights move in straight line */
\r
9172 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9173 mid.x = start.x + (finish.x - start.x) / 2;
\r
9174 mid.y = start.y + (finish.y - start.y) / 2;
\r
9176 /* Knight: make diagonal movement then straight */
\r
9177 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9178 mid.x = start.x + (finish.x - start.x) / 2;
\r
9182 mid.y = start.y + (finish.y - start.y) / 2;
\r
9186 /* Don't use as many frames for very short moves */
\r
9187 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9188 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9190 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9192 animInfo.from.x = fromX;
\r
9193 animInfo.from.y = fromY;
\r
9194 animInfo.to.x = toX;
\r
9195 animInfo.to.y = toY;
\r
9196 animInfo.lastpos = start;
\r
9197 animInfo.piece = piece;
\r
9198 for (n = 0; n < nFrames; n++) {
\r
9199 animInfo.pos = frames[n];
\r
9200 DrawPosition(FALSE, NULL);
\r
9201 animInfo.lastpos = animInfo.pos;
\r
9202 Sleep(appData.animSpeed);
\r
9204 animInfo.pos = finish;
\r
9205 DrawPosition(FALSE, NULL);
\r
9206 animInfo.piece = EmptySquare;
\r
9207 if(gameInfo.variant == VariantAtomic &&
\r
9208 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9209 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9212 /* Convert board position to corner of screen rect and color */
\r
9215 ScreenSquare(column, row, pt)
\r
9216 int column; int row; POINT * pt;
\r
9219 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9220 pt->y = lineGap + row * (squareSize + lineGap);
\r
9222 pt->x = lineGap + column * (squareSize + lineGap);
\r
9223 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9227 /* Generate a series of frame coords from start->mid->finish.
\r
9228 The movement rate doubles until the half way point is
\r
9229 reached, then halves back down to the final destination,
\r
9230 which gives a nice slow in/out effect. The algorithmn
\r
9231 may seem to generate too many intermediates for short
\r
9232 moves, but remember that the purpose is to attract the
\r
9233 viewers attention to the piece about to be moved and
\r
9234 then to where it ends up. Too few frames would be less
\r
9238 Tween(start, mid, finish, factor, frames, nFrames)
\r
9239 POINT * start; POINT * mid;
\r
9240 POINT * finish; int factor;
\r
9241 POINT frames[]; int * nFrames;
\r
9243 int n, fraction = 1, count = 0;
\r
9245 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9246 for (n = 0; n < factor; n++)
\r
9248 for (n = 0; n < factor; n++) {
\r
9249 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9250 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9252 fraction = fraction / 2;
\r
9256 frames[count] = *mid;
\r
9259 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9261 for (n = 0; n < factor; n++) {
\r
9262 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9263 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9265 fraction = fraction * 2;
\r
9271 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9273 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9275 EvalGraphSet( first, last, current, pvInfoList );
\r