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 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
195 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
196 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
197 static HPEN gridPen = NULL;
\r
198 static HPEN highlightPen = NULL;
\r
199 static HPEN premovePen = NULL;
\r
200 static NPLOGPALETTE pLogPal;
\r
201 static BOOL paletteChanged = FALSE;
\r
202 static HICON iconWhite, iconBlack, iconCurrent;
\r
203 static int doingSizing = FALSE;
\r
204 static int lastSizing = 0;
\r
205 static int prevStderrPort;
\r
206 static HBITMAP userLogo;
\r
208 static HBITMAP liteBackTexture = NULL;
\r
209 static HBITMAP darkBackTexture = NULL;
\r
210 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
211 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
212 static int backTextureSquareSize = 0;
\r
213 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
215 #if __GNUC__ && !defined(_winmajor)
\r
216 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
218 #if defined(_winmajor)
\r
219 #define oldDialog (_winmajor < 4)
\r
221 #define oldDialog 0
\r
231 int cliWidth, cliHeight;
\r
234 SizeInfo sizeInfo[] =
\r
236 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
237 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
238 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
239 { "petite", 33, 1, 1, 1, 0, 0 },
\r
240 { "slim", 37, 2, 1, 0, 0, 0 },
\r
241 { "small", 40, 2, 1, 0, 0, 0 },
\r
242 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
243 { "middling", 49, 2, 0, 0, 0, 0 },
\r
244 { "average", 54, 2, 0, 0, 0, 0 },
\r
245 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
246 { "medium", 64, 3, 0, 0, 0, 0 },
\r
247 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
248 { "large", 80, 3, 0, 0, 0, 0 },
\r
249 { "big", 87, 3, 0, 0, 0, 0 },
\r
250 { "huge", 95, 3, 0, 0, 0, 0 },
\r
251 { "giant", 108, 3, 0, 0, 0, 0 },
\r
252 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
253 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
254 { NULL, 0, 0, 0, 0, 0, 0 }
\r
257 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
258 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
260 { 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
261 { 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
262 { 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
263 { 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
264 { 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
265 { 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
266 { 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
267 { 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
268 { 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
269 { 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
270 { 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
271 { 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
272 { 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
273 { 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
274 { 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
275 { 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
276 { 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
277 { 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
280 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
289 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
290 #define N_BUTTONS 5
\r
292 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
294 {"<<", IDM_ToStart, NULL, NULL},
\r
295 {"<", IDM_Backward, NULL, NULL},
\r
296 {"P", IDM_Pause, NULL, NULL},
\r
297 {">", IDM_Forward, NULL, NULL},
\r
298 {">>", IDM_ToEnd, NULL, NULL},
\r
301 int tinyLayout = 0, smallLayout = 0;
\r
302 #define MENU_BAR_ITEMS 7
\r
303 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
304 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
305 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
309 MySound sounds[(int)NSoundClasses];
\r
310 MyTextAttribs textAttribs[(int)NColorClasses];
\r
312 MyColorizeAttribs colorizeAttribs[] = {
\r
313 { (COLORREF)0, 0, "Shout Text" },
\r
314 { (COLORREF)0, 0, "SShout/CShout" },
\r
315 { (COLORREF)0, 0, "Channel 1 Text" },
\r
316 { (COLORREF)0, 0, "Channel Text" },
\r
317 { (COLORREF)0, 0, "Kibitz Text" },
\r
318 { (COLORREF)0, 0, "Tell Text" },
\r
319 { (COLORREF)0, 0, "Challenge Text" },
\r
320 { (COLORREF)0, 0, "Request Text" },
\r
321 { (COLORREF)0, 0, "Seek Text" },
\r
322 { (COLORREF)0, 0, "Normal Text" },
\r
323 { (COLORREF)0, 0, "None" }
\r
328 static char *commentTitle;
\r
329 static char *commentText;
\r
330 static int commentIndex;
\r
331 static Boolean editComment = FALSE;
\r
334 char errorTitle[MSG_SIZ];
\r
335 char errorMessage[2*MSG_SIZ];
\r
336 HWND errorDialog = NULL;
\r
337 BOOLEAN moveErrorMessageUp = FALSE;
\r
338 BOOLEAN consoleEcho = TRUE;
\r
339 CHARFORMAT consoleCF;
\r
340 COLORREF consoleBackgroundColor;
\r
342 char *programVersion;
\r
348 typedef int CPKind;
\r
357 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
360 #define INPUT_SOURCE_BUF_SIZE 4096
\r
362 typedef struct _InputSource {
\r
369 char buf[INPUT_SOURCE_BUF_SIZE];
\r
373 InputCallback func;
\r
374 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
378 InputSource *consoleInputSource;
\r
383 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
384 VOID ConsoleCreate();
\r
386 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
387 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
388 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
389 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
391 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
392 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
393 void ParseIcsTextMenu(char *icsTextMenuString);
\r
394 VOID PopUpMoveDialog(char firstchar);
\r
395 VOID PopUpNameDialog(char firstchar);
\r
396 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
400 int GameListOptions();
\r
402 int dummy; // [HGM] for obsolete args
\r
404 HWND hwndMain = NULL; /* root window*/
\r
405 HWND hwndConsole = NULL;
\r
406 HWND commentDialog = NULL;
\r
407 HWND moveHistoryDialog = NULL;
\r
408 HWND evalGraphDialog = NULL;
\r
409 HWND engineOutputDialog = NULL;
\r
410 HWND gameListDialog = NULL;
\r
411 HWND editTagsDialog = NULL;
\r
413 int commentUp = FALSE;
\r
415 WindowPlacement wpMain;
\r
416 WindowPlacement wpConsole;
\r
417 WindowPlacement wpComment;
\r
418 WindowPlacement wpMoveHistory;
\r
419 WindowPlacement wpEvalGraph;
\r
420 WindowPlacement wpEngineOutput;
\r
421 WindowPlacement wpGameList;
\r
422 WindowPlacement wpTags;
\r
424 VOID EngineOptionsPopup(); // [HGM] settings
\r
426 VOID GothicPopUp(char *title, VariantClass variant);
\r
428 * Setting "frozen" should disable all user input other than deleting
\r
429 * the window. We do this while engines are initializing themselves.
\r
431 static int frozen = 0;
\r
432 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
438 if (frozen) return;
\r
440 hmenu = GetMenu(hwndMain);
\r
441 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
442 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
444 DrawMenuBar(hwndMain);
\r
447 /* Undo a FreezeUI */
\r
453 if (!frozen) return;
\r
455 hmenu = GetMenu(hwndMain);
\r
456 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
457 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
459 DrawMenuBar(hwndMain);
\r
462 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
464 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
470 #define JAWS_ALT_INTERCEPT
\r
471 #define JAWS_KB_NAVIGATION
\r
472 #define JAWS_MENU_ITEMS
\r
473 #define JAWS_SILENCE
\r
474 #define JAWS_REPLAY
\r
476 #define JAWS_COPYRIGHT
\r
477 #define JAWS_DELETE(X) X
\r
478 #define SAYMACHINEMOVE()
\r
482 /*---------------------------------------------------------------------------*\
\r
486 \*---------------------------------------------------------------------------*/
\r
489 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
490 LPSTR lpCmdLine, int nCmdShow)
\r
493 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
494 // INITCOMMONCONTROLSEX ex;
\r
498 LoadLibrary("RICHED32.DLL");
\r
499 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
501 if (!InitApplication(hInstance)) {
\r
504 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
510 // InitCommonControlsEx(&ex);
\r
511 InitCommonControls();
\r
513 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
514 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
515 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
517 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
519 while (GetMessage(&msg, /* message structure */
\r
520 NULL, /* handle of window receiving the message */
\r
521 0, /* lowest message to examine */
\r
522 0)) /* highest message to examine */
\r
525 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
526 // [HGM] navigate: switch between all windows with tab
\r
527 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
528 int i, currentElement = 0;
\r
530 // first determine what element of the chain we come from (if any)
\r
531 if(appData.icsActive) {
\r
532 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
533 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
535 if(engineOutputDialog && EngineOutputIsUp()) {
\r
536 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
537 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
539 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
540 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
542 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
543 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
544 if(msg.hwnd == e1) currentElement = 2; else
\r
545 if(msg.hwnd == e2) currentElement = 3; else
\r
546 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
547 if(msg.hwnd == mh) currentElement = 4; else
\r
548 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
549 if(msg.hwnd == hText) currentElement = 5; else
\r
550 if(msg.hwnd == hInput) currentElement = 6; else
\r
551 for (i = 0; i < N_BUTTONS; i++) {
\r
552 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
555 // determine where to go to
\r
556 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
558 currentElement = (currentElement + direction) % 7;
\r
559 switch(currentElement) {
\r
561 h = hwndMain; break; // passing this case always makes the loop exit
\r
563 h = buttonDesc[0].hwnd; break; // could be NULL
\r
565 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
568 if(!EngineOutputIsUp()) continue;
\r
571 if(!MoveHistoryIsUp()) continue;
\r
573 // case 6: // input to eval graph does not seem to get here!
\r
574 // if(!EvalGraphIsUp()) continue;
\r
575 // h = evalGraphDialog; break;
\r
577 if(!appData.icsActive) continue;
\r
581 if(!appData.icsActive) continue;
\r
587 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
588 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
591 continue; // this message now has been processed
\r
595 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
596 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
597 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
598 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
599 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
600 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
601 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
602 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
603 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
604 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
605 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
606 for(i=0; i<MAX_CHAT; i++)
\r
607 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
610 if(done) continue; // [HGM] chat: end patch
\r
611 TranslateMessage(&msg); /* Translates virtual key codes */
\r
612 DispatchMessage(&msg); /* Dispatches message to window */
\r
617 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
620 /*---------------------------------------------------------------------------*\
\r
622 * Initialization functions
\r
624 \*---------------------------------------------------------------------------*/
\r
628 { // update user logo if necessary
\r
629 static char oldUserName[MSG_SIZ], *curName;
\r
631 if(appData.autoLogo) {
\r
632 curName = UserName();
\r
633 if(strcmp(curName, oldUserName)) {
\r
634 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
635 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
636 strcpy(oldUserName, curName);
\r
642 InitApplication(HINSTANCE hInstance)
\r
646 /* Fill in window class structure with parameters that describe the */
\r
649 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
650 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
651 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
652 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
653 wc.hInstance = hInstance; /* Owner of this class */
\r
654 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
655 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
656 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
657 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
658 wc.lpszClassName = szAppName; /* Name to register as */
\r
660 /* Register the window class and return success/failure code. */
\r
661 if (!RegisterClass(&wc)) return FALSE;
\r
663 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
664 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
666 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
667 wc.hInstance = hInstance;
\r
668 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
669 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
670 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
671 wc.lpszMenuName = NULL;
\r
672 wc.lpszClassName = szConsoleName;
\r
674 if (!RegisterClass(&wc)) return FALSE;
\r
679 /* Set by InitInstance, used by EnsureOnScreen */
\r
680 int screenHeight, screenWidth;
\r
683 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
685 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
686 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
687 if (*x > screenWidth - 32) *x = 0;
\r
688 if (*y > screenHeight - 32) *y = 0;
\r
689 if (*x < minX) *x = minX;
\r
690 if (*y < minY) *y = minY;
\r
694 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
696 HWND hwnd; /* Main window handle. */
\r
698 WINDOWPLACEMENT wp;
\r
701 hInst = hInstance; /* Store instance handle in our global variable */
\r
702 programName = szAppName;
\r
704 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
705 *filepart = NULLCHAR;
\r
707 GetCurrentDirectory(MSG_SIZ, installDir);
\r
709 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
710 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
711 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
712 /* xboard, and older WinBoards, controlled the move sound with the
\r
713 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
714 always turn the option on (so that the backend will call us),
\r
715 then let the user turn the sound off by setting it to silence if
\r
716 desired. To accommodate old winboard.ini files saved by old
\r
717 versions of WinBoard, we also turn off the sound if the option
\r
718 was initially set to false. [HGM] taken out of InitAppData */
\r
719 if (!appData.ringBellAfterMoves) {
\r
720 sounds[(int)SoundMove].name = strdup("");
\r
721 appData.ringBellAfterMoves = TRUE;
\r
723 if (appData.debugMode) {
\r
724 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
725 setbuf(debugFP, NULL);
\r
730 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
731 // InitEngineUCI( installDir, &second );
\r
733 /* Create a main window for this application instance. */
\r
734 hwnd = CreateWindow(szAppName, szTitle,
\r
735 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
736 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
737 NULL, NULL, hInstance, NULL);
\r
740 /* If window could not be created, return "failure" */
\r
745 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
746 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
747 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
749 if (first.programLogo == NULL && appData.debugMode) {
\r
750 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
752 } else if(appData.autoLogo) {
\r
753 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
755 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
756 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
760 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
761 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
763 if (second.programLogo == NULL && appData.debugMode) {
\r
764 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
766 } else if(appData.autoLogo) {
\r
768 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
769 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
770 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
772 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
773 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
774 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
780 iconWhite = LoadIcon(hInstance, "icon_white");
\r
781 iconBlack = LoadIcon(hInstance, "icon_black");
\r
782 iconCurrent = iconWhite;
\r
783 InitDrawingColors();
\r
784 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
785 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
786 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
787 /* Compute window size for each board size, and use the largest
\r
788 size that fits on this screen as the default. */
\r
789 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
790 if (boardSize == (BoardSize)-1 &&
\r
791 winH <= screenHeight
\r
792 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
793 && winW <= screenWidth) {
\r
794 boardSize = (BoardSize)ibs;
\r
798 InitDrawingSizes(boardSize, 0);
\r
800 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
802 /* [AS] Load textures if specified */
\r
803 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
805 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
806 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
807 liteBackTextureMode = appData.liteBackTextureMode;
\r
809 if (liteBackTexture == NULL && appData.debugMode) {
\r
810 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
814 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
815 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
816 darkBackTextureMode = appData.darkBackTextureMode;
\r
818 if (darkBackTexture == NULL && appData.debugMode) {
\r
819 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
823 mysrandom( (unsigned) time(NULL) );
\r
825 /* [AS] Restore layout */
\r
826 if( wpMoveHistory.visible ) {
\r
827 MoveHistoryPopUp();
\r
830 if( wpEvalGraph.visible ) {
\r
834 if( wpEngineOutput.visible ) {
\r
835 EngineOutputPopUp();
\r
840 /* Make the window visible; update its client area; and return "success" */
\r
841 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
842 wp.length = sizeof(WINDOWPLACEMENT);
\r
844 wp.showCmd = nCmdShow;
\r
845 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
846 wp.rcNormalPosition.left = wpMain.x;
\r
847 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
848 wp.rcNormalPosition.top = wpMain.y;
\r
849 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
850 SetWindowPlacement(hwndMain, &wp);
\r
852 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
853 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
857 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
858 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
860 ShowWindow(hwndConsole, nCmdShow);
\r
862 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
863 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
872 HMENU hmenu = GetMenu(hwndMain);
\r
874 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
875 MF_BYCOMMAND|((appData.icsActive &&
\r
876 *appData.icsCommPort != NULLCHAR) ?
\r
877 MF_ENABLED : MF_GRAYED));
\r
878 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
879 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
880 MF_CHECKED : MF_UNCHECKED));
\r
883 //---------------------------------------------------------------------------------------------------------
\r
885 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
886 #define XBOARD FALSE
\r
888 #define OPTCHAR "/"
\r
889 #define SEPCHAR "="
\r
893 // front-end part of option handling
\r
896 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
898 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
899 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
902 lf->lfEscapement = 0;
\r
903 lf->lfOrientation = 0;
\r
904 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
905 lf->lfItalic = mfp->italic;
\r
906 lf->lfUnderline = mfp->underline;
\r
907 lf->lfStrikeOut = mfp->strikeout;
\r
908 lf->lfCharSet = mfp->charset;
\r
909 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
910 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
911 lf->lfQuality = DEFAULT_QUALITY;
\r
912 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
913 strcpy(lf->lfFaceName, mfp->faceName);
\r
917 CreateFontInMF(MyFont *mf)
\r
919 LFfromMFP(&mf->lf, &mf->mfp);
\r
920 if (mf->hf) DeleteObject(mf->hf);
\r
921 mf->hf = CreateFontIndirect(&mf->lf);
\r
924 // [HGM] This platform-dependent table provides the location for storing the color info
\r
926 colorVariable[] = {
\r
931 &highlightSquareColor,
\r
932 &premoveHighlightColor,
\r
934 &consoleBackgroundColor,
\r
935 &appData.fontForeColorWhite,
\r
936 &appData.fontBackColorWhite,
\r
937 &appData.fontForeColorBlack,
\r
938 &appData.fontBackColorBlack,
\r
939 &appData.evalHistColorWhite,
\r
940 &appData.evalHistColorBlack,
\r
941 &appData.highlightArrowColor,
\r
944 /* Command line font name parser. NULL name means do nothing.
\r
945 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
946 For backward compatibility, syntax without the colon is also
\r
947 accepted, but font names with digits in them won't work in that case.
\r
950 ParseFontName(char *name, MyFontParams *mfp)
\r
953 if (name == NULL) return;
\r
955 q = strchr(p, ':');
\r
957 if (q - p >= sizeof(mfp->faceName))
\r
958 ExitArgError("Font name too long:", name);
\r
959 memcpy(mfp->faceName, p, q - p);
\r
960 mfp->faceName[q - p] = NULLCHAR;
\r
964 while (*p && !isdigit(*p)) {
\r
966 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
967 ExitArgError("Font name too long:", name);
\r
969 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
972 if (!*p) ExitArgError("Font point size missing:", name);
\r
973 mfp->pointSize = (float) atof(p);
\r
974 mfp->bold = (strchr(p, 'b') != NULL);
\r
975 mfp->italic = (strchr(p, 'i') != NULL);
\r
976 mfp->underline = (strchr(p, 'u') != NULL);
\r
977 mfp->strikeout = (strchr(p, 's') != NULL);
\r
978 mfp->charset = DEFAULT_CHARSET;
\r
979 q = strchr(p, 'c');
\r
981 mfp->charset = (BYTE) atoi(q+1);
\r
985 ParseFont(char *name, int number)
\r
986 { // wrapper to shield back-end from 'font'
\r
987 ParseFontName(name, &font[boardSize][number]->mfp);
\r
992 { // in WB we have a 2D array of fonts; this initializes their description
\r
994 /* Point font array elements to structures and
\r
995 parse default font names */
\r
996 for (i=0; i<NUM_FONTS; i++) {
\r
997 for (j=0; j<NUM_SIZES; j++) {
\r
998 font[j][i] = &fontRec[j][i];
\r
999 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1006 { // here we create the actual fonts from the selected descriptions
\r
1008 for (i=0; i<NUM_FONTS; i++) {
\r
1009 for (j=0; j<NUM_SIZES; j++) {
\r
1010 CreateFontInMF(font[j][i]);
\r
1014 /* Color name parser.
\r
1015 X version accepts X color names, but this one
\r
1016 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1018 ParseColorName(char *name)
\r
1020 int red, green, blue, count;
\r
1021 char buf[MSG_SIZ];
\r
1023 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1025 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1026 &red, &green, &blue);
\r
1029 sprintf(buf, "Can't parse color name %s", name);
\r
1030 DisplayError(buf, 0);
\r
1031 return RGB(0, 0, 0);
\r
1033 return PALETTERGB(red, green, blue);
\r
1037 ParseColor(int n, char *name)
\r
1038 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1039 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1043 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1045 char *e = argValue;
\r
1049 if (*e == 'b') eff |= CFE_BOLD;
\r
1050 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1051 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1052 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1053 else if (*e == '#' || isdigit(*e)) break;
\r
1057 *color = ParseColorName(e);
\r
1061 ParseTextAttribs(ColorClass cc, char *s)
\r
1062 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1063 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1064 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1068 ParseBoardSize(void *addr, char *name)
\r
1069 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1070 BoardSize bs = SizeTiny;
\r
1071 while (sizeInfo[bs].name != NULL) {
\r
1072 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1073 *(BoardSize *)addr = bs;
\r
1078 ExitArgError("Unrecognized board size value", name);
\r
1083 { // [HGM] import name from appData first
\r
1086 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1087 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1088 textAttribs[cc].sound.data = NULL;
\r
1089 MyLoadSound(&textAttribs[cc].sound);
\r
1091 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1092 textAttribs[cc].sound.name = strdup("");
\r
1093 textAttribs[cc].sound.data = NULL;
\r
1095 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1096 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1097 sounds[sc].data = NULL;
\r
1098 MyLoadSound(&sounds[sc]);
\r
1103 SetCommPortDefaults()
\r
1105 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1106 dcb.DCBlength = sizeof(DCB);
\r
1107 dcb.BaudRate = 9600;
\r
1108 dcb.fBinary = TRUE;
\r
1109 dcb.fParity = FALSE;
\r
1110 dcb.fOutxCtsFlow = FALSE;
\r
1111 dcb.fOutxDsrFlow = FALSE;
\r
1112 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1113 dcb.fDsrSensitivity = FALSE;
\r
1114 dcb.fTXContinueOnXoff = TRUE;
\r
1115 dcb.fOutX = FALSE;
\r
1117 dcb.fNull = FALSE;
\r
1118 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1119 dcb.fAbortOnError = FALSE;
\r
1121 dcb.Parity = SPACEPARITY;
\r
1122 dcb.StopBits = ONESTOPBIT;
\r
1125 // [HGM] args: these three cases taken out to stay in front-end
\r
1127 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1128 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1129 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1130 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1132 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1133 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1134 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1135 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1136 ad->argName, mfp->faceName, mfp->pointSize,
\r
1137 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1138 mfp->bold ? "b" : "",
\r
1139 mfp->italic ? "i" : "",
\r
1140 mfp->underline ? "u" : "",
\r
1141 mfp->strikeout ? "s" : "",
\r
1142 (int)mfp->charset);
\r
1148 { // [HGM] copy the names from the internal WB variables to appData
\r
1151 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1152 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1153 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1154 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1158 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1159 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1160 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1161 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1162 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1163 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1164 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1165 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1166 (ta->effects) ? " " : "",
\r
1167 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1171 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1172 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1173 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1174 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1175 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1179 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1180 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1181 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1185 ParseCommPortSettings(char *s)
\r
1186 { // wrapper to keep dcb from back-end
\r
1187 ParseCommSettings(s, &dcb);
\r
1192 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1193 GetActualPlacement(hwndMain, &wpMain);
\r
1194 GetActualPlacement(hwndConsole, &wpConsole);
\r
1195 GetActualPlacement(commentDialog, &wpComment);
\r
1196 GetActualPlacement(editTagsDialog, &wpTags);
\r
1197 GetActualPlacement(gameListDialog, &wpGameList);
\r
1198 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1199 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1200 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1204 PrintCommPortSettings(FILE *f, char *name)
\r
1205 { // wrapper to shield back-end from DCB
\r
1206 PrintCommSettings(f, name, &dcb);
\r
1210 MySearchPath(char *installDir, char *name, char *fullname)
\r
1213 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1217 MyGetFullPathName(char *name, char *fullname)
\r
1220 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1225 { // [HGM] args: allows testing if main window is realized from back-end
\r
1226 return hwndMain != NULL;
\r
1230 PopUpStartupDialog()
\r
1234 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1235 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1236 FreeProcInstance(lpProc);
\r
1239 /*---------------------------------------------------------------------------*\
\r
1241 * GDI board drawing routines
\r
1243 \*---------------------------------------------------------------------------*/
\r
1245 /* [AS] Draw square using background texture */
\r
1246 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1251 return; /* Should never happen! */
\r
1254 SetGraphicsMode( dst, GM_ADVANCED );
\r
1261 /* X reflection */
\r
1266 x.eDx = (FLOAT) dw + dx - 1;
\r
1269 SetWorldTransform( dst, &x );
\r
1272 /* Y reflection */
\r
1278 x.eDy = (FLOAT) dh + dy - 1;
\r
1280 SetWorldTransform( dst, &x );
\r
1288 x.eDx = (FLOAT) dx;
\r
1289 x.eDy = (FLOAT) dy;
\r
1292 SetWorldTransform( dst, &x );
\r
1296 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1304 SetWorldTransform( dst, &x );
\r
1306 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1309 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1311 PM_WP = (int) WhitePawn,
\r
1312 PM_WN = (int) WhiteKnight,
\r
1313 PM_WB = (int) WhiteBishop,
\r
1314 PM_WR = (int) WhiteRook,
\r
1315 PM_WQ = (int) WhiteQueen,
\r
1316 PM_WF = (int) WhiteFerz,
\r
1317 PM_WW = (int) WhiteWazir,
\r
1318 PM_WE = (int) WhiteAlfil,
\r
1319 PM_WM = (int) WhiteMan,
\r
1320 PM_WO = (int) WhiteCannon,
\r
1321 PM_WU = (int) WhiteUnicorn,
\r
1322 PM_WH = (int) WhiteNightrider,
\r
1323 PM_WA = (int) WhiteAngel,
\r
1324 PM_WC = (int) WhiteMarshall,
\r
1325 PM_WAB = (int) WhiteCardinal,
\r
1326 PM_WD = (int) WhiteDragon,
\r
1327 PM_WL = (int) WhiteLance,
\r
1328 PM_WS = (int) WhiteCobra,
\r
1329 PM_WV = (int) WhiteFalcon,
\r
1330 PM_WSG = (int) WhiteSilver,
\r
1331 PM_WG = (int) WhiteGrasshopper,
\r
1332 PM_WK = (int) WhiteKing,
\r
1333 PM_BP = (int) BlackPawn,
\r
1334 PM_BN = (int) BlackKnight,
\r
1335 PM_BB = (int) BlackBishop,
\r
1336 PM_BR = (int) BlackRook,
\r
1337 PM_BQ = (int) BlackQueen,
\r
1338 PM_BF = (int) BlackFerz,
\r
1339 PM_BW = (int) BlackWazir,
\r
1340 PM_BE = (int) BlackAlfil,
\r
1341 PM_BM = (int) BlackMan,
\r
1342 PM_BO = (int) BlackCannon,
\r
1343 PM_BU = (int) BlackUnicorn,
\r
1344 PM_BH = (int) BlackNightrider,
\r
1345 PM_BA = (int) BlackAngel,
\r
1346 PM_BC = (int) BlackMarshall,
\r
1347 PM_BG = (int) BlackGrasshopper,
\r
1348 PM_BAB = (int) BlackCardinal,
\r
1349 PM_BD = (int) BlackDragon,
\r
1350 PM_BL = (int) BlackLance,
\r
1351 PM_BS = (int) BlackCobra,
\r
1352 PM_BV = (int) BlackFalcon,
\r
1353 PM_BSG = (int) BlackSilver,
\r
1354 PM_BK = (int) BlackKing
\r
1357 static HFONT hPieceFont = NULL;
\r
1358 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1359 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1360 static int fontBitmapSquareSize = 0;
\r
1361 static char pieceToFontChar[(int) EmptySquare] =
\r
1362 { 'p', 'n', 'b', 'r', 'q',
\r
1363 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1364 'k', 'o', 'm', 'v', 't', 'w',
\r
1365 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1368 extern BOOL SetCharTable( char *table, const char * map );
\r
1369 /* [HGM] moved to backend.c */
\r
1371 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1374 BYTE r1 = GetRValue( color );
\r
1375 BYTE g1 = GetGValue( color );
\r
1376 BYTE b1 = GetBValue( color );
\r
1382 /* Create a uniform background first */
\r
1383 hbrush = CreateSolidBrush( color );
\r
1384 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1385 FillRect( hdc, &rc, hbrush );
\r
1386 DeleteObject( hbrush );
\r
1389 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1390 int steps = squareSize / 2;
\r
1393 for( i=0; i<steps; i++ ) {
\r
1394 BYTE r = r1 - (r1-r2) * i / steps;
\r
1395 BYTE g = g1 - (g1-g2) * i / steps;
\r
1396 BYTE b = b1 - (b1-b2) * i / steps;
\r
1398 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1399 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1400 FillRect( hdc, &rc, hbrush );
\r
1401 DeleteObject(hbrush);
\r
1404 else if( mode == 2 ) {
\r
1405 /* Diagonal gradient, good more or less for every piece */
\r
1406 POINT triangle[3];
\r
1407 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1408 HBRUSH hbrush_old;
\r
1409 int steps = squareSize;
\r
1412 triangle[0].x = squareSize - steps;
\r
1413 triangle[0].y = squareSize;
\r
1414 triangle[1].x = squareSize;
\r
1415 triangle[1].y = squareSize;
\r
1416 triangle[2].x = squareSize;
\r
1417 triangle[2].y = squareSize - steps;
\r
1419 for( i=0; i<steps; i++ ) {
\r
1420 BYTE r = r1 - (r1-r2) * i / steps;
\r
1421 BYTE g = g1 - (g1-g2) * i / steps;
\r
1422 BYTE b = b1 - (b1-b2) * i / steps;
\r
1424 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1425 hbrush_old = SelectObject( hdc, hbrush );
\r
1426 Polygon( hdc, triangle, 3 );
\r
1427 SelectObject( hdc, hbrush_old );
\r
1428 DeleteObject(hbrush);
\r
1433 SelectObject( hdc, hpen );
\r
1438 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1439 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1440 piece: follow the steps as explained below.
\r
1442 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1446 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1450 int backColor = whitePieceColor;
\r
1451 int foreColor = blackPieceColor;
\r
1453 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1454 backColor = appData.fontBackColorWhite;
\r
1455 foreColor = appData.fontForeColorWhite;
\r
1457 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1458 backColor = appData.fontBackColorBlack;
\r
1459 foreColor = appData.fontForeColorBlack;
\r
1463 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1465 hbm_old = SelectObject( hdc, hbm );
\r
1469 rc.right = squareSize;
\r
1470 rc.bottom = squareSize;
\r
1472 /* Step 1: background is now black */
\r
1473 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1475 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1477 pt.x = (squareSize - sz.cx) / 2;
\r
1478 pt.y = (squareSize - sz.cy) / 2;
\r
1480 SetBkMode( hdc, TRANSPARENT );
\r
1481 SetTextColor( hdc, chroma );
\r
1482 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1483 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1485 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1486 /* Step 3: the area outside the piece is filled with white */
\r
1487 // FloodFill( hdc, 0, 0, chroma );
\r
1488 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1489 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1490 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1491 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1492 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1494 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1495 but if the start point is not inside the piece we're lost!
\r
1496 There should be a better way to do this... if we could create a region or path
\r
1497 from the fill operation we would be fine for example.
\r
1499 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1500 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1502 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1503 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1504 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1506 SelectObject( dc2, bm2 );
\r
1507 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1508 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1509 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1510 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1511 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1514 DeleteObject( bm2 );
\r
1517 SetTextColor( hdc, 0 );
\r
1519 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1520 draw the piece again in black for safety.
\r
1522 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1524 SelectObject( hdc, hbm_old );
\r
1526 if( hPieceMask[index] != NULL ) {
\r
1527 DeleteObject( hPieceMask[index] );
\r
1530 hPieceMask[index] = hbm;
\r
1533 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1535 SelectObject( hdc, hbm );
\r
1538 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1539 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1540 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1542 SelectObject( dc1, hPieceMask[index] );
\r
1543 SelectObject( dc2, bm2 );
\r
1544 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1545 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1548 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1549 the piece background and deletes (makes transparent) the rest.
\r
1550 Thanks to that mask, we are free to paint the background with the greates
\r
1551 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1552 We use this, to make gradients and give the pieces a "roundish" look.
\r
1554 SetPieceBackground( hdc, backColor, 2 );
\r
1555 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1559 DeleteObject( bm2 );
\r
1562 SetTextColor( hdc, foreColor );
\r
1563 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1565 SelectObject( hdc, hbm_old );
\r
1567 if( hPieceFace[index] != NULL ) {
\r
1568 DeleteObject( hPieceFace[index] );
\r
1571 hPieceFace[index] = hbm;
\r
1574 static int TranslatePieceToFontPiece( int piece )
\r
1604 case BlackMarshall:
\r
1608 case BlackNightrider:
\r
1614 case BlackUnicorn:
\r
1618 case BlackGrasshopper:
\r
1630 case BlackCardinal:
\r
1637 case WhiteMarshall:
\r
1641 case WhiteNightrider:
\r
1647 case WhiteUnicorn:
\r
1651 case WhiteGrasshopper:
\r
1663 case WhiteCardinal:
\r
1672 void CreatePiecesFromFont()
\r
1675 HDC hdc_window = NULL;
\r
1681 if( fontBitmapSquareSize < 0 ) {
\r
1682 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1686 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1687 fontBitmapSquareSize = -1;
\r
1691 if( fontBitmapSquareSize != squareSize ) {
\r
1692 hdc_window = GetDC( hwndMain );
\r
1693 hdc = CreateCompatibleDC( hdc_window );
\r
1695 if( hPieceFont != NULL ) {
\r
1696 DeleteObject( hPieceFont );
\r
1699 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1700 hPieceMask[i] = NULL;
\r
1701 hPieceFace[i] = NULL;
\r
1707 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1708 fontHeight = appData.fontPieceSize;
\r
1711 fontHeight = (fontHeight * squareSize) / 100;
\r
1713 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1715 lf.lfEscapement = 0;
\r
1716 lf.lfOrientation = 0;
\r
1717 lf.lfWeight = FW_NORMAL;
\r
1719 lf.lfUnderline = 0;
\r
1720 lf.lfStrikeOut = 0;
\r
1721 lf.lfCharSet = DEFAULT_CHARSET;
\r
1722 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1723 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1724 lf.lfQuality = PROOF_QUALITY;
\r
1725 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1726 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1727 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1729 hPieceFont = CreateFontIndirect( &lf );
\r
1731 if( hPieceFont == NULL ) {
\r
1732 fontBitmapSquareSize = -2;
\r
1735 /* Setup font-to-piece character table */
\r
1736 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1737 /* No (or wrong) global settings, try to detect the font */
\r
1738 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1740 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1742 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1743 /* DiagramTT* family */
\r
1744 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1746 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1747 /* Fairy symbols */
\r
1748 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1750 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1751 /* Good Companion (Some characters get warped as literal :-( */
\r
1752 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1753 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1754 SetCharTable(pieceToFontChar, s);
\r
1757 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1758 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1762 /* Create bitmaps */
\r
1763 hfont_old = SelectObject( hdc, hPieceFont );
\r
1764 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1765 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1766 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1768 SelectObject( hdc, hfont_old );
\r
1770 fontBitmapSquareSize = squareSize;
\r
1774 if( hdc != NULL ) {
\r
1778 if( hdc_window != NULL ) {
\r
1779 ReleaseDC( hwndMain, hdc_window );
\r
1784 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1788 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1789 if (gameInfo.event &&
\r
1790 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1791 strcmp(name, "k80s") == 0) {
\r
1792 strcpy(name, "tim");
\r
1794 return LoadBitmap(hinst, name);
\r
1798 /* Insert a color into the program's logical palette
\r
1799 structure. This code assumes the given color is
\r
1800 the result of the RGB or PALETTERGB macro, and it
\r
1801 knows how those macros work (which is documented).
\r
1804 InsertInPalette(COLORREF color)
\r
1806 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1808 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1809 DisplayFatalError("Too many colors", 0, 1);
\r
1810 pLogPal->palNumEntries--;
\r
1814 pe->peFlags = (char) 0;
\r
1815 pe->peRed = (char) (0xFF & color);
\r
1816 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1817 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1823 InitDrawingColors()
\r
1825 if (pLogPal == NULL) {
\r
1826 /* Allocate enough memory for a logical palette with
\r
1827 * PALETTESIZE entries and set the size and version fields
\r
1828 * of the logical palette structure.
\r
1830 pLogPal = (NPLOGPALETTE)
\r
1831 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1832 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1833 pLogPal->palVersion = 0x300;
\r
1835 pLogPal->palNumEntries = 0;
\r
1837 InsertInPalette(lightSquareColor);
\r
1838 InsertInPalette(darkSquareColor);
\r
1839 InsertInPalette(whitePieceColor);
\r
1840 InsertInPalette(blackPieceColor);
\r
1841 InsertInPalette(highlightSquareColor);
\r
1842 InsertInPalette(premoveHighlightColor);
\r
1844 /* create a logical color palette according the information
\r
1845 * in the LOGPALETTE structure.
\r
1847 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1849 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1850 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1851 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1852 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1853 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1854 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1855 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1856 /* [AS] Force rendering of the font-based pieces */
\r
1857 if( fontBitmapSquareSize > 0 ) {
\r
1858 fontBitmapSquareSize = 0;
\r
1864 BoardWidth(int boardSize, int n)
\r
1865 { /* [HGM] argument n added to allow different width and height */
\r
1866 int lineGap = sizeInfo[boardSize].lineGap;
\r
1868 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1869 lineGap = appData.overrideLineGap;
\r
1872 return (n + 1) * lineGap +
\r
1873 n * sizeInfo[boardSize].squareSize;
\r
1876 /* Respond to board resize by dragging edge */
\r
1878 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1880 BoardSize newSize = NUM_SIZES - 1;
\r
1881 static int recurse = 0;
\r
1882 if (IsIconic(hwndMain)) return;
\r
1883 if (recurse > 0) return;
\r
1885 while (newSize > 0) {
\r
1886 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1887 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1888 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1891 boardSize = newSize;
\r
1892 InitDrawingSizes(boardSize, flags);
\r
1899 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1901 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1902 ChessSquare piece;
\r
1903 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1905 SIZE clockSize, messageSize;
\r
1907 char buf[MSG_SIZ];
\r
1909 HMENU hmenu = GetMenu(hwndMain);
\r
1910 RECT crect, wrect, oldRect;
\r
1912 LOGBRUSH logbrush;
\r
1914 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1915 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1917 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1918 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1920 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1921 oldRect.top = wpMain.y;
\r
1922 oldRect.right = wpMain.x + wpMain.width;
\r
1923 oldRect.bottom = wpMain.y + wpMain.height;
\r
1925 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1926 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1927 squareSize = sizeInfo[boardSize].squareSize;
\r
1928 lineGap = sizeInfo[boardSize].lineGap;
\r
1929 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1931 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1932 lineGap = appData.overrideLineGap;
\r
1935 if (tinyLayout != oldTinyLayout) {
\r
1936 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1938 style &= ~WS_SYSMENU;
\r
1939 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1940 "&Minimize\tCtrl+F4");
\r
1942 style |= WS_SYSMENU;
\r
1943 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1945 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1947 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1948 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1949 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1951 DrawMenuBar(hwndMain);
\r
1954 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1955 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1957 /* Get text area sizes */
\r
1958 hdc = GetDC(hwndMain);
\r
1959 if (appData.clockMode) {
\r
1960 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1962 sprintf(buf, "White");
\r
1964 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1965 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1966 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1967 str = "We only care about the height here";
\r
1968 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1969 SelectObject(hdc, oldFont);
\r
1970 ReleaseDC(hwndMain, hdc);
\r
1972 /* Compute where everything goes */
\r
1973 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1974 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1975 logoHeight = 2*clockSize.cy;
\r
1976 leftLogoRect.left = OUTER_MARGIN;
\r
1977 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1978 leftLogoRect.top = OUTER_MARGIN;
\r
1979 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1981 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1982 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1983 rightLogoRect.top = OUTER_MARGIN;
\r
1984 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1987 whiteRect.left = leftLogoRect.right;
\r
1988 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
1989 whiteRect.top = OUTER_MARGIN;
\r
1990 whiteRect.bottom = whiteRect.top + logoHeight;
\r
1992 blackRect.right = rightLogoRect.left;
\r
1993 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
1994 blackRect.top = whiteRect.top;
\r
1995 blackRect.bottom = whiteRect.bottom;
\r
1997 whiteRect.left = OUTER_MARGIN;
\r
1998 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
1999 whiteRect.top = OUTER_MARGIN;
\r
2000 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2002 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2003 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2004 blackRect.top = whiteRect.top;
\r
2005 blackRect.bottom = whiteRect.bottom;
\r
2008 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2009 if (appData.showButtonBar) {
\r
2010 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2011 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2013 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2015 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2016 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2018 boardRect.left = OUTER_MARGIN;
\r
2019 boardRect.right = boardRect.left + boardWidth;
\r
2020 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2021 boardRect.bottom = boardRect.top + boardHeight;
\r
2023 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2024 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2025 oldBoardSize = boardSize;
\r
2026 oldTinyLayout = tinyLayout;
\r
2027 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2028 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2029 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2030 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2031 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2032 wpMain.height = winH; // without disturbing window attachments
\r
2033 GetWindowRect(hwndMain, &wrect);
\r
2034 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2035 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2037 // [HGM] placement: let attached windows follow size change.
\r
2038 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2039 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2040 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2041 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2042 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2044 /* compensate if menu bar wrapped */
\r
2045 GetClientRect(hwndMain, &crect);
\r
2046 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2047 wpMain.height += offby;
\r
2049 case WMSZ_TOPLEFT:
\r
2050 SetWindowPos(hwndMain, NULL,
\r
2051 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2052 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2055 case WMSZ_TOPRIGHT:
\r
2057 SetWindowPos(hwndMain, NULL,
\r
2058 wrect.left, wrect.bottom - wpMain.height,
\r
2059 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2062 case WMSZ_BOTTOMLEFT:
\r
2064 SetWindowPos(hwndMain, NULL,
\r
2065 wrect.right - wpMain.width, wrect.top,
\r
2066 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2069 case WMSZ_BOTTOMRIGHT:
\r
2073 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2074 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2079 for (i = 0; i < N_BUTTONS; i++) {
\r
2080 if (buttonDesc[i].hwnd != NULL) {
\r
2081 DestroyWindow(buttonDesc[i].hwnd);
\r
2082 buttonDesc[i].hwnd = NULL;
\r
2084 if (appData.showButtonBar) {
\r
2085 buttonDesc[i].hwnd =
\r
2086 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2087 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2088 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2089 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2090 (HMENU) buttonDesc[i].id,
\r
2091 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2093 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2094 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2095 MAKELPARAM(FALSE, 0));
\r
2097 if (buttonDesc[i].id == IDM_Pause)
\r
2098 hwndPause = buttonDesc[i].hwnd;
\r
2099 buttonDesc[i].wndproc = (WNDPROC)
\r
2100 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2103 if (gridPen != NULL) DeleteObject(gridPen);
\r
2104 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2105 if (premovePen != NULL) DeleteObject(premovePen);
\r
2106 if (lineGap != 0) {
\r
2107 logbrush.lbStyle = BS_SOLID;
\r
2108 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2110 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2111 lineGap, &logbrush, 0, NULL);
\r
2112 logbrush.lbColor = highlightSquareColor;
\r
2114 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2115 lineGap, &logbrush, 0, NULL);
\r
2117 logbrush.lbColor = premoveHighlightColor;
\r
2119 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2120 lineGap, &logbrush, 0, NULL);
\r
2122 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2123 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2124 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2125 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2126 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2127 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2128 BOARD_WIDTH * (squareSize + lineGap);
\r
2129 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2131 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2132 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2133 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2134 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2135 lineGap / 2 + (i * (squareSize + lineGap));
\r
2136 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2137 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2138 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2142 /* [HGM] Licensing requirement */
\r
2144 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2147 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2149 GothicPopUp( "", VariantNormal);
\r
2152 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2154 /* Load piece bitmaps for this board size */
\r
2155 for (i=0; i<=2; i++) {
\r
2156 for (piece = WhitePawn;
\r
2157 (int) piece < (int) BlackPawn;
\r
2158 piece = (ChessSquare) ((int) piece + 1)) {
\r
2159 if (pieceBitmap[i][piece] != NULL)
\r
2160 DeleteObject(pieceBitmap[i][piece]);
\r
2164 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2165 // Orthodox Chess pieces
\r
2166 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2167 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2168 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2169 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2170 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2171 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2172 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2173 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2174 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2175 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2176 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2177 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2178 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2179 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2180 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2181 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2182 // in Shogi, Hijack the unused Queen for Lance
\r
2183 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2184 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2185 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2187 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2188 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2189 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2192 if(squareSize <= 72 && squareSize >= 33) {
\r
2193 /* A & C are available in most sizes now */
\r
2194 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2195 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2196 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2197 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2198 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2199 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2200 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2201 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2202 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2203 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2204 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2205 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2206 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2207 } else { // Smirf-like
\r
2208 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2209 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2210 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2212 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2213 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2214 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2215 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2216 } else { // WinBoard standard
\r
2217 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2218 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2219 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2224 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2225 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2226 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2227 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2228 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2229 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2230 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2231 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2232 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2233 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2234 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2235 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2236 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2237 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2238 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2239 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2240 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2241 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2242 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2243 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2244 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2245 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2246 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2247 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2248 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2249 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2250 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2251 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2252 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2253 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2254 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2256 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2257 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2258 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2259 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2260 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2261 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2262 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2263 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2264 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2265 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2266 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2267 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2268 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2270 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2271 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2272 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2273 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2274 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2275 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2276 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2277 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2278 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2279 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2280 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2281 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2284 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2285 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2286 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2287 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2288 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2289 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2290 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2291 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2292 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2293 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2294 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2295 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2296 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2297 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2298 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2302 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2303 /* special Shogi support in this size */
\r
2304 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2305 for (piece = WhitePawn;
\r
2306 (int) piece < (int) BlackPawn;
\r
2307 piece = (ChessSquare) ((int) piece + 1)) {
\r
2308 if (pieceBitmap[i][piece] != NULL)
\r
2309 DeleteObject(pieceBitmap[i][piece]);
\r
2312 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2313 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2314 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2315 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2316 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2317 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2318 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2319 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2320 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2321 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2322 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2323 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2324 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2325 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2326 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2327 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2328 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2329 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2330 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2331 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2332 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2333 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2334 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2335 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2336 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2337 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2338 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2339 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2340 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2341 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2342 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2343 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2344 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2345 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2346 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2347 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2348 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2349 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2350 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2351 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2352 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2353 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2359 PieceBitmap(ChessSquare p, int kind)
\r
2361 if ((int) p >= (int) BlackPawn)
\r
2362 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2364 return pieceBitmap[kind][(int) p];
\r
2367 /***************************************************************/
\r
2369 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2370 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2372 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2373 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2377 SquareToPos(int row, int column, int * x, int * y)
\r
2380 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2381 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2383 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2384 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2389 DrawCoordsOnDC(HDC hdc)
\r
2391 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
2392 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
2393 char str[2] = { NULLCHAR, NULLCHAR };
\r
2394 int oldMode, oldAlign, x, y, start, i;
\r
2398 if (!appData.showCoords)
\r
2401 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2403 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2404 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2405 oldAlign = GetTextAlign(hdc);
\r
2406 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2408 y = boardRect.top + lineGap;
\r
2409 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2411 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2412 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2413 str[0] = files[start + i];
\r
2414 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2415 y += squareSize + lineGap;
\r
2418 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2420 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2421 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2422 str[0] = ranks[start + i];
\r
2423 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2424 x += squareSize + lineGap;
\r
2427 SelectObject(hdc, oldBrush);
\r
2428 SetBkMode(hdc, oldMode);
\r
2429 SetTextAlign(hdc, oldAlign);
\r
2430 SelectObject(hdc, oldFont);
\r
2434 DrawGridOnDC(HDC hdc)
\r
2438 if (lineGap != 0) {
\r
2439 oldPen = SelectObject(hdc, gridPen);
\r
2440 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2441 SelectObject(hdc, oldPen);
\r
2445 #define HIGHLIGHT_PEN 0
\r
2446 #define PREMOVE_PEN 1
\r
2449 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2452 HPEN oldPen, hPen;
\r
2453 if (lineGap == 0) return;
\r
2455 x1 = boardRect.left +
\r
2456 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2457 y1 = boardRect.top +
\r
2458 lineGap/2 + y * (squareSize + lineGap);
\r
2460 x1 = boardRect.left +
\r
2461 lineGap/2 + x * (squareSize + lineGap);
\r
2462 y1 = boardRect.top +
\r
2463 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2465 hPen = pen ? premovePen : highlightPen;
\r
2466 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2467 MoveToEx(hdc, x1, y1, NULL);
\r
2468 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2469 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2470 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2471 LineTo(hdc, x1, y1);
\r
2472 SelectObject(hdc, oldPen);
\r
2476 DrawHighlightsOnDC(HDC hdc)
\r
2479 for (i=0; i<2; i++) {
\r
2480 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2481 DrawHighlightOnDC(hdc, TRUE,
\r
2482 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2485 for (i=0; i<2; i++) {
\r
2486 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2487 premoveHighlightInfo.sq[i].y >= 0) {
\r
2488 DrawHighlightOnDC(hdc, TRUE,
\r
2489 premoveHighlightInfo.sq[i].x,
\r
2490 premoveHighlightInfo.sq[i].y,
\r
2496 /* Note: sqcolor is used only in monoMode */
\r
2497 /* Note that this code is largely duplicated in woptions.c,
\r
2498 function DrawSampleSquare, so that needs to be updated too */
\r
2500 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2502 HBITMAP oldBitmap;
\r
2506 if (appData.blindfold) return;
\r
2508 /* [AS] Use font-based pieces if needed */
\r
2509 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2510 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2511 CreatePiecesFromFont();
\r
2513 if( fontBitmapSquareSize == squareSize ) {
\r
2514 int index = TranslatePieceToFontPiece(piece);
\r
2516 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2520 squareSize, squareSize,
\r
2525 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2529 squareSize, squareSize,
\r
2538 if (appData.monoMode) {
\r
2539 SelectObject(tmphdc, PieceBitmap(piece,
\r
2540 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2541 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2542 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2544 tmpSize = squareSize;
\r
2546 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2547 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2548 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2549 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2550 x += (squareSize - minorSize)>>1;
\r
2551 y += squareSize - minorSize - 2;
\r
2552 tmpSize = minorSize;
\r
2554 if (color || appData.allWhite ) {
\r
2555 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2557 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2558 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2559 if(appData.upsideDown && color==flipView)
\r
2560 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2562 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2563 /* Use black for outline of white pieces */
\r
2564 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2565 if(appData.upsideDown && color==flipView)
\r
2566 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2568 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2570 /* Use square color for details of black pieces */
\r
2571 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2572 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2573 if(appData.upsideDown && !flipView)
\r
2574 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2576 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2578 SelectObject(hdc, oldBrush);
\r
2579 SelectObject(tmphdc, oldBitmap);
\r
2583 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2584 int GetBackTextureMode( int algo )
\r
2586 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2590 case BACK_TEXTURE_MODE_PLAIN:
\r
2591 result = 1; /* Always use identity map */
\r
2593 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2594 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2602 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2603 to handle redraws cleanly (as random numbers would always be different).
\r
2605 VOID RebuildTextureSquareInfo()
\r
2615 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2617 if( liteBackTexture != NULL ) {
\r
2618 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2619 lite_w = bi.bmWidth;
\r
2620 lite_h = bi.bmHeight;
\r
2624 if( darkBackTexture != NULL ) {
\r
2625 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2626 dark_w = bi.bmWidth;
\r
2627 dark_h = bi.bmHeight;
\r
2631 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2632 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2633 if( (col + row) & 1 ) {
\r
2635 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2636 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2637 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2638 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2643 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2644 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2645 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2646 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2653 /* [AS] Arrow highlighting support */
\r
2655 static int A_WIDTH = 5; /* Width of arrow body */
\r
2657 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2658 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2660 static double Sqr( double x )
\r
2665 static int Round( double x )
\r
2667 return (int) (x + 0.5);
\r
2670 /* Draw an arrow between two points using current settings */
\r
2671 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2674 double dx, dy, j, k, x, y;
\r
2676 if( d_x == s_x ) {
\r
2677 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2679 arrow[0].x = s_x + A_WIDTH;
\r
2682 arrow[1].x = s_x + A_WIDTH;
\r
2683 arrow[1].y = d_y - h;
\r
2685 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2686 arrow[2].y = d_y - h;
\r
2691 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2692 arrow[4].y = d_y - h;
\r
2694 arrow[5].x = s_x - A_WIDTH;
\r
2695 arrow[5].y = d_y - h;
\r
2697 arrow[6].x = s_x - A_WIDTH;
\r
2700 else if( d_y == s_y ) {
\r
2701 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2704 arrow[0].y = s_y + A_WIDTH;
\r
2706 arrow[1].x = d_x - w;
\r
2707 arrow[1].y = s_y + A_WIDTH;
\r
2709 arrow[2].x = d_x - w;
\r
2710 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2715 arrow[4].x = d_x - w;
\r
2716 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2718 arrow[5].x = d_x - w;
\r
2719 arrow[5].y = s_y - A_WIDTH;
\r
2722 arrow[6].y = s_y - A_WIDTH;
\r
2725 /* [AS] Needed a lot of paper for this! :-) */
\r
2726 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2727 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2729 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2731 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2736 arrow[0].x = Round(x - j);
\r
2737 arrow[0].y = Round(y + j*dx);
\r
2739 arrow[1].x = Round(x + j);
\r
2740 arrow[1].y = Round(y - j*dx);
\r
2743 x = (double) d_x - k;
\r
2744 y = (double) d_y - k*dy;
\r
2747 x = (double) d_x + k;
\r
2748 y = (double) d_y + k*dy;
\r
2751 arrow[2].x = Round(x + j);
\r
2752 arrow[2].y = Round(y - j*dx);
\r
2754 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2755 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2760 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2761 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2763 arrow[6].x = Round(x - j);
\r
2764 arrow[6].y = Round(y + j*dx);
\r
2767 Polygon( hdc, arrow, 7 );
\r
2770 /* [AS] Draw an arrow between two squares */
\r
2771 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2773 int s_x, s_y, d_x, d_y;
\r
2780 if( s_col == d_col && s_row == d_row ) {
\r
2784 /* Get source and destination points */
\r
2785 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2786 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2789 d_y += squareSize / 4;
\r
2791 else if( d_y < s_y ) {
\r
2792 d_y += 3 * squareSize / 4;
\r
2795 d_y += squareSize / 2;
\r
2799 d_x += squareSize / 4;
\r
2801 else if( d_x < s_x ) {
\r
2802 d_x += 3 * squareSize / 4;
\r
2805 d_x += squareSize / 2;
\r
2808 s_x += squareSize / 2;
\r
2809 s_y += squareSize / 2;
\r
2811 /* Adjust width */
\r
2812 A_WIDTH = squareSize / 14;
\r
2815 stLB.lbStyle = BS_SOLID;
\r
2816 stLB.lbColor = appData.highlightArrowColor;
\r
2819 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2820 holdpen = SelectObject( hdc, hpen );
\r
2821 hbrush = CreateBrushIndirect( &stLB );
\r
2822 holdbrush = SelectObject( hdc, hbrush );
\r
2824 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2826 SelectObject( hdc, holdpen );
\r
2827 SelectObject( hdc, holdbrush );
\r
2828 DeleteObject( hpen );
\r
2829 DeleteObject( hbrush );
\r
2832 BOOL HasHighlightInfo()
\r
2834 BOOL result = FALSE;
\r
2836 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2837 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2845 BOOL IsDrawArrowEnabled()
\r
2847 BOOL result = FALSE;
\r
2849 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2856 VOID DrawArrowHighlight( HDC hdc )
\r
2858 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2859 DrawArrowBetweenSquares( hdc,
\r
2860 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2861 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2865 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2867 HRGN result = NULL;
\r
2869 if( HasHighlightInfo() ) {
\r
2870 int x1, y1, x2, y2;
\r
2871 int sx, sy, dx, dy;
\r
2873 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2874 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2876 sx = MIN( x1, x2 );
\r
2877 sy = MIN( y1, y2 );
\r
2878 dx = MAX( x1, x2 ) + squareSize;
\r
2879 dy = MAX( y1, y2 ) + squareSize;
\r
2881 result = CreateRectRgn( sx, sy, dx, dy );
\r
2888 Warning: this function modifies the behavior of several other functions.
\r
2890 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2891 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2892 repaint is scattered all over the place, which is not good for features such as
\r
2893 "arrow highlighting" that require a full repaint of the board.
\r
2895 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2896 user interaction, when speed is not so important) but especially to avoid errors
\r
2897 in the displayed graphics.
\r
2899 In such patched places, I always try refer to this function so there is a single
\r
2900 place to maintain knowledge.
\r
2902 To restore the original behavior, just return FALSE unconditionally.
\r
2904 BOOL IsFullRepaintPreferrable()
\r
2906 BOOL result = FALSE;
\r
2908 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2909 /* Arrow may appear on the board */
\r
2917 This function is called by DrawPosition to know whether a full repaint must
\r
2920 Only DrawPosition may directly call this function, which makes use of
\r
2921 some state information. Other function should call DrawPosition specifying
\r
2922 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2924 BOOL DrawPositionNeedsFullRepaint()
\r
2926 BOOL result = FALSE;
\r
2929 Probably a slightly better policy would be to trigger a full repaint
\r
2930 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2931 but animation is fast enough that it's difficult to notice.
\r
2933 if( animInfo.piece == EmptySquare ) {
\r
2934 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2943 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2945 int row, column, x, y, square_color, piece_color;
\r
2946 ChessSquare piece;
\r
2948 HDC texture_hdc = NULL;
\r
2950 /* [AS] Initialize background textures if needed */
\r
2951 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2952 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2953 if( backTextureSquareSize != squareSize
\r
2954 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2955 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2956 backTextureSquareSize = squareSize;
\r
2957 RebuildTextureSquareInfo();
\r
2960 texture_hdc = CreateCompatibleDC( hdc );
\r
2963 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2964 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2966 SquareToPos(row, column, &x, &y);
\r
2968 piece = board[row][column];
\r
2970 square_color = ((column + row) % 2) == 1;
\r
2971 if( gameInfo.variant == VariantXiangqi ) {
\r
2972 square_color = !InPalace(row, column);
\r
2973 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2974 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2976 piece_color = (int) piece < (int) BlackPawn;
\r
2979 /* [HGM] holdings file: light square or black */
\r
2980 if(column == BOARD_LEFT-2) {
\r
2981 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2984 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
2988 if(column == BOARD_RGHT + 1 ) {
\r
2989 if( row < gameInfo.holdingsSize )
\r
2992 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
2996 if(column == BOARD_LEFT-1 ) /* left align */
\r
2997 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
2998 else if( column == BOARD_RGHT) /* right align */
\r
2999 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3001 if (appData.monoMode) {
\r
3002 if (piece == EmptySquare) {
\r
3003 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3004 square_color ? WHITENESS : BLACKNESS);
\r
3006 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3009 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3010 /* [AS] Draw the square using a texture bitmap */
\r
3011 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3012 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3013 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3016 squareSize, squareSize,
\r
3019 backTextureSquareInfo[r][c].mode,
\r
3020 backTextureSquareInfo[r][c].x,
\r
3021 backTextureSquareInfo[r][c].y );
\r
3023 SelectObject( texture_hdc, hbm );
\r
3025 if (piece != EmptySquare) {
\r
3026 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3030 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3032 oldBrush = SelectObject(hdc, brush );
\r
3033 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3034 SelectObject(hdc, oldBrush);
\r
3035 if (piece != EmptySquare)
\r
3036 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3041 if( texture_hdc != NULL ) {
\r
3042 DeleteDC( texture_hdc );
\r
3046 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3047 void fputDW(FILE *f, int x)
\r
3049 fputc(x & 255, f);
\r
3050 fputc(x>>8 & 255, f);
\r
3051 fputc(x>>16 & 255, f);
\r
3052 fputc(x>>24 & 255, f);
\r
3055 #define MAX_CLIPS 200 /* more than enough */
\r
3058 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3060 // HBITMAP bufferBitmap;
\r
3065 int w = 100, h = 50;
\r
3067 if(logo == NULL) return;
\r
3068 // GetClientRect(hwndMain, &Rect);
\r
3069 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3070 // Rect.bottom-Rect.top+1);
\r
3071 tmphdc = CreateCompatibleDC(hdc);
\r
3072 hbm = SelectObject(tmphdc, logo);
\r
3073 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3077 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3078 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3079 SelectObject(tmphdc, hbm);
\r
3084 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3086 static Board lastReq, lastDrawn;
\r
3087 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3088 static int lastDrawnFlipView = 0;
\r
3089 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3090 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3093 HBITMAP bufferBitmap;
\r
3094 HBITMAP oldBitmap;
\r
3096 HRGN clips[MAX_CLIPS];
\r
3097 ChessSquare dragged_piece = EmptySquare;
\r
3099 /* I'm undecided on this - this function figures out whether a full
\r
3100 * repaint is necessary on its own, so there's no real reason to have the
\r
3101 * caller tell it that. I think this can safely be set to FALSE - but
\r
3102 * if we trust the callers not to request full repaints unnessesarily, then
\r
3103 * we could skip some clipping work. In other words, only request a full
\r
3104 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3105 * gamestart and similar) --Hawk
\r
3107 Boolean fullrepaint = repaint;
\r
3109 if( DrawPositionNeedsFullRepaint() ) {
\r
3110 fullrepaint = TRUE;
\r
3113 if (board == NULL) {
\r
3114 if (!lastReqValid) {
\r
3119 CopyBoard(lastReq, board);
\r
3123 if (doingSizing) {
\r
3127 if (IsIconic(hwndMain)) {
\r
3131 if (hdc == NULL) {
\r
3132 hdc = GetDC(hwndMain);
\r
3133 if (!appData.monoMode) {
\r
3134 SelectPalette(hdc, hPal, FALSE);
\r
3135 RealizePalette(hdc);
\r
3139 releaseDC = FALSE;
\r
3142 /* Create some work-DCs */
\r
3143 hdcmem = CreateCompatibleDC(hdc);
\r
3144 tmphdc = CreateCompatibleDC(hdc);
\r
3146 /* If dragging is in progress, we temporarely remove the piece */
\r
3147 /* [HGM] or temporarily decrease count if stacked */
\r
3148 /* !! Moved to before board compare !! */
\r
3149 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3150 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3151 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3152 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3153 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3155 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3156 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3157 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3159 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3162 /* Figure out which squares need updating by comparing the
\r
3163 * newest board with the last drawn board and checking if
\r
3164 * flipping has changed.
\r
3166 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3167 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3168 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3169 if (lastDrawn[row][column] != board[row][column]) {
\r
3170 SquareToPos(row, column, &x, &y);
\r
3171 clips[num_clips++] =
\r
3172 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3176 for (i=0; i<2; i++) {
\r
3177 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3178 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3179 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3180 lastDrawnHighlight.sq[i].y >= 0) {
\r
3181 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3182 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3183 clips[num_clips++] =
\r
3184 CreateRectRgn(x - lineGap, y - lineGap,
\r
3185 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3187 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3188 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3189 clips[num_clips++] =
\r
3190 CreateRectRgn(x - lineGap, y - lineGap,
\r
3191 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3195 for (i=0; i<2; i++) {
\r
3196 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3197 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3198 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3199 lastDrawnPremove.sq[i].y >= 0) {
\r
3200 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3201 lastDrawnPremove.sq[i].x, &x, &y);
\r
3202 clips[num_clips++] =
\r
3203 CreateRectRgn(x - lineGap, y - lineGap,
\r
3204 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3206 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3207 premoveHighlightInfo.sq[i].y >= 0) {
\r
3208 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3209 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3210 clips[num_clips++] =
\r
3211 CreateRectRgn(x - lineGap, y - lineGap,
\r
3212 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3217 fullrepaint = TRUE;
\r
3220 /* Create a buffer bitmap - this is the actual bitmap
\r
3221 * being written to. When all the work is done, we can
\r
3222 * copy it to the real DC (the screen). This avoids
\r
3223 * the problems with flickering.
\r
3225 GetClientRect(hwndMain, &Rect);
\r
3226 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3227 Rect.bottom-Rect.top+1);
\r
3228 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3229 if (!appData.monoMode) {
\r
3230 SelectPalette(hdcmem, hPal, FALSE);
\r
3233 /* Create clips for dragging */
\r
3234 if (!fullrepaint) {
\r
3235 if (dragInfo.from.x >= 0) {
\r
3236 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3237 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3239 if (dragInfo.start.x >= 0) {
\r
3240 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3241 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3243 if (dragInfo.pos.x >= 0) {
\r
3244 x = dragInfo.pos.x - squareSize / 2;
\r
3245 y = dragInfo.pos.y - squareSize / 2;
\r
3246 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3248 if (dragInfo.lastpos.x >= 0) {
\r
3249 x = dragInfo.lastpos.x - squareSize / 2;
\r
3250 y = dragInfo.lastpos.y - squareSize / 2;
\r
3251 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3255 /* Are we animating a move?
\r
3257 * - remove the piece from the board (temporarely)
\r
3258 * - calculate the clipping region
\r
3260 if (!fullrepaint) {
\r
3261 if (animInfo.piece != EmptySquare) {
\r
3262 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3263 x = boardRect.left + animInfo.lastpos.x;
\r
3264 y = boardRect.top + animInfo.lastpos.y;
\r
3265 x2 = boardRect.left + animInfo.pos.x;
\r
3266 y2 = boardRect.top + animInfo.pos.y;
\r
3267 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3268 /* Slight kludge. The real problem is that after AnimateMove is
\r
3269 done, the position on the screen does not match lastDrawn.
\r
3270 This currently causes trouble only on e.p. captures in
\r
3271 atomic, where the piece moves to an empty square and then
\r
3272 explodes. The old and new positions both had an empty square
\r
3273 at the destination, but animation has drawn a piece there and
\r
3274 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3275 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3279 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3280 if (num_clips == 0)
\r
3281 fullrepaint = TRUE;
\r
3283 /* Set clipping on the memory DC */
\r
3284 if (!fullrepaint) {
\r
3285 SelectClipRgn(hdcmem, clips[0]);
\r
3286 for (x = 1; x < num_clips; x++) {
\r
3287 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3288 abort(); // this should never ever happen!
\r
3292 /* Do all the drawing to the memory DC */
\r
3293 if(explodeInfo.radius) { // [HGM] atomic
\r
3295 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3296 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3297 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3298 x += squareSize/2;
\r
3299 y += squareSize/2;
\r
3300 if(!fullrepaint) {
\r
3301 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3302 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3304 DrawGridOnDC(hdcmem);
\r
3305 DrawHighlightsOnDC(hdcmem);
\r
3306 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3307 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3308 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3309 SelectObject(hdcmem, oldBrush);
\r
3311 DrawGridOnDC(hdcmem);
\r
3312 DrawHighlightsOnDC(hdcmem);
\r
3313 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3316 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3317 if(appData.autoLogo) {
\r
3319 switch(gameMode) { // pick logos based on game mode
\r
3320 case IcsObserving:
\r
3321 whiteLogo = second.programLogo; // ICS logo
\r
3322 blackLogo = second.programLogo;
\r
3325 case IcsPlayingWhite:
\r
3326 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3327 blackLogo = second.programLogo; // ICS logo
\r
3329 case IcsPlayingBlack:
\r
3330 whiteLogo = second.programLogo; // ICS logo
\r
3331 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3333 case TwoMachinesPlay:
\r
3334 if(first.twoMachinesColor[0] == 'b') {
\r
3335 whiteLogo = second.programLogo;
\r
3336 blackLogo = first.programLogo;
\r
3339 case MachinePlaysWhite:
\r
3340 blackLogo = userLogo;
\r
3342 case MachinePlaysBlack:
\r
3343 whiteLogo = userLogo;
\r
3344 blackLogo = first.programLogo;
\r
3347 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3348 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3351 if( appData.highlightMoveWithArrow ) {
\r
3352 DrawArrowHighlight(hdcmem);
\r
3355 DrawCoordsOnDC(hdcmem);
\r
3357 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3358 /* to make sure lastDrawn contains what is actually drawn */
\r
3360 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3361 if (dragged_piece != EmptySquare) {
\r
3362 /* [HGM] or restack */
\r
3363 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3364 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3366 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3367 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3368 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3369 x = dragInfo.pos.x - squareSize / 2;
\r
3370 y = dragInfo.pos.y - squareSize / 2;
\r
3371 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3372 ((int) dragged_piece < (int) BlackPawn),
\r
3373 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3376 /* Put the animated piece back into place and draw it */
\r
3377 if (animInfo.piece != EmptySquare) {
\r
3378 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3379 x = boardRect.left + animInfo.pos.x;
\r
3380 y = boardRect.top + animInfo.pos.y;
\r
3381 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3382 ((int) animInfo.piece < (int) BlackPawn),
\r
3383 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3386 /* Release the bufferBitmap by selecting in the old bitmap
\r
3387 * and delete the memory DC
\r
3389 SelectObject(hdcmem, oldBitmap);
\r
3392 /* Set clipping on the target DC */
\r
3393 if (!fullrepaint) {
\r
3394 SelectClipRgn(hdc, clips[0]);
\r
3395 for (x = 1; x < num_clips; x++) {
\r
3396 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3397 abort(); // this should never ever happen!
\r
3401 /* Copy the new bitmap onto the screen in one go.
\r
3402 * This way we avoid any flickering
\r
3404 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3405 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3406 boardRect.right - boardRect.left,
\r
3407 boardRect.bottom - boardRect.top,
\r
3408 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3409 if(saveDiagFlag) {
\r
3410 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3411 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3413 GetObject(bufferBitmap, sizeof(b), &b);
\r
3414 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3415 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3416 bih.biWidth = b.bmWidth;
\r
3417 bih.biHeight = b.bmHeight;
\r
3419 bih.biBitCount = b.bmBitsPixel;
\r
3420 bih.biCompression = 0;
\r
3421 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3422 bih.biXPelsPerMeter = 0;
\r
3423 bih.biYPelsPerMeter = 0;
\r
3424 bih.biClrUsed = 0;
\r
3425 bih.biClrImportant = 0;
\r
3426 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3427 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3428 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3429 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3431 wb = b.bmWidthBytes;
\r
3433 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3434 int k = ((int*) pData)[i];
\r
3435 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3436 if(j >= 16) break;
\r
3438 if(j >= nrColors) nrColors = j+1;
\r
3440 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3442 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3443 for(w=0; w<(wb>>2); w+=2) {
\r
3444 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3445 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3446 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3447 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3448 pData[p++] = m | j<<4;
\r
3450 while(p&3) pData[p++] = 0;
\r
3453 wb = ((wb+31)>>5)<<2;
\r
3455 // write BITMAPFILEHEADER
\r
3456 fprintf(diagFile, "BM");
\r
3457 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3458 fputDW(diagFile, 0);
\r
3459 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3460 // write BITMAPINFOHEADER
\r
3461 fputDW(diagFile, 40);
\r
3462 fputDW(diagFile, b.bmWidth);
\r
3463 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3464 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3465 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3466 fputDW(diagFile, 0);
\r
3467 fputDW(diagFile, 0);
\r
3468 fputDW(diagFile, 0);
\r
3469 fputDW(diagFile, 0);
\r
3470 fputDW(diagFile, 0);
\r
3471 fputDW(diagFile, 0);
\r
3472 // write color table
\r
3474 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3475 // write bitmap data
\r
3476 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3477 fputc(pData[i], diagFile);
\r
3481 SelectObject(tmphdc, oldBitmap);
\r
3483 /* Massive cleanup */
\r
3484 for (x = 0; x < num_clips; x++)
\r
3485 DeleteObject(clips[x]);
\r
3488 DeleteObject(bufferBitmap);
\r
3491 ReleaseDC(hwndMain, hdc);
\r
3493 if (lastDrawnFlipView != flipView) {
\r
3495 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3497 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3500 /* CopyBoard(lastDrawn, board);*/
\r
3501 lastDrawnHighlight = highlightInfo;
\r
3502 lastDrawnPremove = premoveHighlightInfo;
\r
3503 lastDrawnFlipView = flipView;
\r
3504 lastDrawnValid = 1;
\r
3507 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3512 saveDiagFlag = 1; diagFile = f;
\r
3513 HDCDrawPosition(NULL, TRUE, NULL);
\r
3517 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3524 /*---------------------------------------------------------------------------*\
\r
3525 | CLIENT PAINT PROCEDURE
\r
3526 | This is the main event-handler for the WM_PAINT message.
\r
3528 \*---------------------------------------------------------------------------*/
\r
3530 PaintProc(HWND hwnd)
\r
3536 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3537 if (IsIconic(hwnd)) {
\r
3538 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3540 if (!appData.monoMode) {
\r
3541 SelectPalette(hdc, hPal, FALSE);
\r
3542 RealizePalette(hdc);
\r
3544 HDCDrawPosition(hdc, 1, NULL);
\r
3546 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3547 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3548 ETO_CLIPPED|ETO_OPAQUE,
\r
3549 &messageRect, messageText, strlen(messageText), NULL);
\r
3550 SelectObject(hdc, oldFont);
\r
3551 DisplayBothClocks();
\r
3553 EndPaint(hwnd,&ps);
\r
3561 * If the user selects on a border boundary, return -1; if off the board,
\r
3562 * return -2. Otherwise map the event coordinate to the square.
\r
3563 * The offset boardRect.left or boardRect.top must already have been
\r
3564 * subtracted from x.
\r
3566 int EventToSquare(x, limit)
\r
3574 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3576 x /= (squareSize + lineGap);
\r
3588 DropEnable dropEnables[] = {
\r
3589 { 'P', DP_Pawn, "Pawn" },
\r
3590 { 'N', DP_Knight, "Knight" },
\r
3591 { 'B', DP_Bishop, "Bishop" },
\r
3592 { 'R', DP_Rook, "Rook" },
\r
3593 { 'Q', DP_Queen, "Queen" },
\r
3597 SetupDropMenu(HMENU hmenu)
\r
3599 int i, count, enable;
\r
3601 extern char white_holding[], black_holding[];
\r
3602 char item[MSG_SIZ];
\r
3604 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3605 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3606 dropEnables[i].piece);
\r
3608 while (p && *p++ == dropEnables[i].piece) count++;
\r
3609 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3610 enable = count > 0 || !appData.testLegality
\r
3611 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3612 && !appData.icsActive);
\r
3613 ModifyMenu(hmenu, dropEnables[i].command,
\r
3614 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3615 dropEnables[i].command, item);
\r
3619 void DragPieceBegin(int x, int y)
\r
3621 dragInfo.lastpos.x = boardRect.left + x;
\r
3622 dragInfo.lastpos.y = boardRect.top + y;
\r
3623 dragInfo.from.x = fromX;
\r
3624 dragInfo.from.y = fromY;
\r
3625 dragInfo.start = dragInfo.from;
\r
3626 SetCapture(hwndMain);
\r
3629 void DragPieceEnd(int x, int y)
\r
3632 dragInfo.start.x = dragInfo.start.y = -1;
\r
3633 dragInfo.from = dragInfo.start;
\r
3634 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3637 /* Event handler for mouse messages */
\r
3639 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3643 static int recursive = 0;
\r
3645 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3648 if (message == WM_MBUTTONUP) {
\r
3649 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3650 to the middle button: we simulate pressing the left button too!
\r
3652 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3653 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3659 pt.x = LOWORD(lParam);
\r
3660 pt.y = HIWORD(lParam);
\r
3661 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3662 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3663 if (!flipView && y >= 0) {
\r
3664 y = BOARD_HEIGHT - 1 - y;
\r
3666 if (flipView && x >= 0) {
\r
3667 x = BOARD_WIDTH - 1 - x;
\r
3670 switch (message) {
\r
3671 case WM_LBUTTONDOWN:
\r
3672 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3673 if (gameMode == EditPosition) {
\r
3674 SetWhiteToPlayEvent();
\r
3675 } else if (gameMode == IcsPlayingBlack ||
\r
3676 gameMode == MachinePlaysWhite) {
\r
3678 } else if (gameMode == EditGame) {
\r
3679 AdjustClock(flipClock, -1);
\r
3681 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3682 if (gameMode == EditPosition) {
\r
3683 SetBlackToPlayEvent();
\r
3684 } else if (gameMode == IcsPlayingWhite ||
\r
3685 gameMode == MachinePlaysBlack) {
\r
3687 } else if (gameMode == EditGame) {
\r
3688 AdjustClock(!flipClock, -1);
\r
3691 dragInfo.start.x = dragInfo.start.y = -1;
\r
3692 dragInfo.from = dragInfo.start;
\r
3693 if(fromX == -1 && frozen) { // not sure where this is for
\r
3694 fromX = fromY = -1;
\r
3695 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3698 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3699 DrawPosition(TRUE, NULL);
\r
3702 case WM_LBUTTONUP:
\r
3703 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3704 DrawPosition(TRUE, NULL);
\r
3707 case WM_MOUSEMOVE:
\r
3708 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
3709 if ((appData.animateDragging || appData.highlightDragging)
\r
3710 && (wParam & MK_LBUTTON)
\r
3711 && dragInfo.from.x >= 0)
\r
3713 BOOL full_repaint = FALSE;
\r
3715 if (appData.animateDragging) {
\r
3716 dragInfo.pos = pt;
\r
3718 if (appData.highlightDragging) {
\r
3719 SetHighlights(fromX, fromY, x, y);
\r
3720 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3721 full_repaint = TRUE;
\r
3725 DrawPosition( full_repaint, NULL);
\r
3727 dragInfo.lastpos = dragInfo.pos;
\r
3731 case WM_MOUSEWHEEL: // [DM]
\r
3732 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3733 /* Mouse Wheel is being rolled forward
\r
3734 * Play moves forward
\r
3736 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3737 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3738 /* Mouse Wheel is being rolled backward
\r
3739 * Play moves backward
\r
3741 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3742 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3752 case WM_MBUTTONDOWN:
\r
3753 case WM_RBUTTONDOWN:
\r
3756 fromX = fromY = -1;
\r
3757 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3758 dragInfo.start.x = dragInfo.start.y = -1;
\r
3759 dragInfo.from = dragInfo.start;
\r
3760 dragInfo.lastpos = dragInfo.pos;
\r
3761 if (appData.highlightDragging) {
\r
3762 ClearHighlights();
\r
3765 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3766 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3767 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3768 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3769 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3772 DrawPosition(TRUE, NULL);
\r
3774 switch (gameMode) {
\r
3775 case EditPosition:
\r
3776 case IcsExamining:
\r
3777 if (x < 0 || y < 0) break;
\r
3780 if (message == WM_MBUTTONDOWN) {
\r
3781 buttonCount = 3; /* even if system didn't think so */
\r
3782 if (wParam & MK_SHIFT)
\r
3783 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3785 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3786 } else { /* message == WM_RBUTTONDOWN */
\r
3787 /* Just have one menu, on the right button. Windows users don't
\r
3788 think to try the middle one, and sometimes other software steals
\r
3789 it, or it doesn't really exist. */
\r
3790 if(gameInfo.variant != VariantShogi)
\r
3791 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3793 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3797 if(!appData.icsEngineAnalyze) break;
3798 case IcsPlayingWhite:
3799 case IcsPlayingBlack:
3800 if(!appData.zippyPlay) goto noZip;
3801 case MachinePlaysWhite:
3802 case MachinePlaysBlack:
3803 case TwoMachinesPlay:
3806 if (!appData.dropMenu) {
3807 SetCapture(hwndMain);
3808 LoadPV(pt.x - boardRect.left, pt.y - boardRect.top);
3811 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
3812 gameMode == AnalyzeFile || gameMode == IcsObserving) break;
3815 if (x < 0 || y < 0) break;
3816 if (!appData.dropMenu || appData.testLegality &&
3817 gameInfo.variant != VariantBughouse &&
3818 gameInfo.variant != VariantCrazyhouse) break;
3821 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3822 SetupDropMenu(hmenu);
\r
3823 MenuPopup(hwnd, pt, hmenu, -1);
\r
3834 /* Preprocess messages for buttons in main window */
\r
3836 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3838 int id = GetWindowLong(hwnd, GWL_ID);
\r
3841 for (i=0; i<N_BUTTONS; i++) {
\r
3842 if (buttonDesc[i].id == id) break;
\r
3844 if (i == N_BUTTONS) return 0;
\r
3845 switch (message) {
\r
3850 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3851 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3858 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3861 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3862 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3863 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3864 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3866 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3868 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3869 PopUpMoveDialog((char)wParam);
\r
3875 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3878 /* Process messages for Promotion dialog box */
\r
3880 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3884 switch (message) {
\r
3885 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3886 /* Center the dialog over the application window */
\r
3887 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3888 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3889 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3890 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3891 SW_SHOW : SW_HIDE);
\r
3892 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3893 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3894 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3895 PieceToChar(WhiteAngel) != '~') ||
\r
3896 (PieceToChar(BlackAngel) >= 'A' &&
\r
3897 PieceToChar(BlackAngel) != '~') ) ?
\r
3898 SW_SHOW : SW_HIDE);
\r
3899 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3900 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3901 PieceToChar(WhiteMarshall) != '~') ||
\r
3902 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3903 PieceToChar(BlackMarshall) != '~') ) ?
\r
3904 SW_SHOW : SW_HIDE);
\r
3905 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3906 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3907 gameInfo.variant != VariantShogi ?
\r
3908 SW_SHOW : SW_HIDE);
\r
3909 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3910 gameInfo.variant != VariantShogi ?
\r
3911 SW_SHOW : SW_HIDE);
\r
3912 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3913 gameInfo.variant == VariantShogi ?
\r
3914 SW_SHOW : SW_HIDE);
\r
3915 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3916 gameInfo.variant == VariantShogi ?
\r
3917 SW_SHOW : SW_HIDE);
\r
3918 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3919 gameInfo.variant == VariantSuper ?
\r
3920 SW_SHOW : SW_HIDE);
\r
3923 case WM_COMMAND: /* message: received a command */
\r
3924 switch (LOWORD(wParam)) {
\r
3926 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3927 ClearHighlights();
\r
3928 DrawPosition(FALSE, NULL);
\r
3931 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3934 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3937 promoChar = PieceToChar(BlackRook);
\r
3940 promoChar = PieceToChar(BlackBishop);
\r
3942 case PB_Chancellor:
\r
3943 promoChar = PieceToChar(BlackMarshall);
\r
3945 case PB_Archbishop:
\r
3946 promoChar = PieceToChar(BlackAngel);
\r
3949 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
3954 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3955 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
3956 only show the popup when we are already sure the move is valid or
\r
3957 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
3958 will figure out it is a promotion from the promoChar. */
\r
3959 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
3960 fromX = fromY = -1;
\r
3961 if (!appData.highlightLastMove) {
\r
3962 ClearHighlights();
\r
3963 DrawPosition(FALSE, NULL);
\r
3970 /* Pop up promotion dialog */
\r
3972 PromotionPopup(HWND hwnd)
\r
3976 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
3977 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
3978 hwnd, (DLGPROC)lpProc);
\r
3979 FreeProcInstance(lpProc);
\r
3985 DrawPosition(TRUE, NULL);
\r
3986 PromotionPopup(hwndMain);
\r
3989 /* Toggle ShowThinking */
\r
3991 ToggleShowThinking()
\r
3993 appData.showThinking = !appData.showThinking;
\r
3994 ShowThinkingEvent();
\r
3998 LoadGameDialog(HWND hwnd, char* title)
\r
4002 char fileTitle[MSG_SIZ];
\r
4003 f = OpenFileDialog(hwnd, "rb", "",
\r
4004 appData.oldSaveStyle ? "gam" : "pgn",
\r
4006 title, &number, fileTitle, NULL);
\r
4008 cmailMsgLoaded = FALSE;
\r
4009 if (number == 0) {
\r
4010 int error = GameListBuild(f);
\r
4012 DisplayError("Cannot build game list", error);
\r
4013 } else if (!ListEmpty(&gameList) &&
\r
4014 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4015 GameListPopUp(f, fileTitle);
\r
4018 GameListDestroy();
\r
4021 LoadGame(f, number, fileTitle, FALSE);
\r
4025 int get_term_width()
\r
4030 HFONT hfont, hold_font;
\r
4035 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4039 // get the text metrics
\r
4040 hdc = GetDC(hText);
\r
4041 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4042 if (consoleCF.dwEffects & CFE_BOLD)
\r
4043 lf.lfWeight = FW_BOLD;
\r
4044 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4045 lf.lfItalic = TRUE;
\r
4046 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4047 lf.lfStrikeOut = TRUE;
\r
4048 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4049 lf.lfUnderline = TRUE;
\r
4050 hfont = CreateFontIndirect(&lf);
\r
4051 hold_font = SelectObject(hdc, hfont);
\r
4052 GetTextMetrics(hdc, &tm);
\r
4053 SelectObject(hdc, hold_font);
\r
4054 DeleteObject(hfont);
\r
4055 ReleaseDC(hText, hdc);
\r
4057 // get the rectangle
\r
4058 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4060 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4063 void UpdateICSWidth(HWND hText)
\r
4065 LONG old_width, new_width;
\r
4067 new_width = get_term_width(hText, FALSE);
\r
4068 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4069 if (new_width != old_width)
\r
4071 ics_update_width(new_width);
\r
4072 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4077 ChangedConsoleFont()
\r
4080 CHARRANGE tmpsel, sel;
\r
4081 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4082 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4083 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4086 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4087 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4088 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4089 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4090 * size. This was undocumented in the version of MSVC++ that I had
\r
4091 * when I wrote the code, but is apparently documented now.
\r
4093 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4094 cfmt.bCharSet = f->lf.lfCharSet;
\r
4095 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4096 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4097 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4098 /* Why are the following seemingly needed too? */
\r
4099 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4100 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4101 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4103 tmpsel.cpMax = -1; /*999999?*/
\r
4104 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4105 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4106 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4107 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4109 paraf.cbSize = sizeof(paraf);
\r
4110 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4111 paraf.dxStartIndent = 0;
\r
4112 paraf.dxOffset = WRAP_INDENT;
\r
4113 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4114 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4115 UpdateICSWidth(hText);
\r
4118 /*---------------------------------------------------------------------------*\
\r
4120 * Window Proc for main window
\r
4122 \*---------------------------------------------------------------------------*/
\r
4124 /* Process messages for main window, etc. */
\r
4126 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4129 int wmId, wmEvent;
\r
4133 char fileTitle[MSG_SIZ];
\r
4134 char buf[MSG_SIZ];
\r
4135 static SnapData sd;
\r
4137 switch (message) {
\r
4139 case WM_PAINT: /* message: repaint portion of window */
\r
4143 case WM_ERASEBKGND:
\r
4144 if (IsIconic(hwnd)) {
\r
4145 /* Cheat; change the message */
\r
4146 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4148 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4152 case WM_LBUTTONDOWN:
\r
4153 case WM_MBUTTONDOWN:
\r
4154 case WM_RBUTTONDOWN:
\r
4155 case WM_LBUTTONUP:
\r
4156 case WM_MBUTTONUP:
\r
4157 case WM_RBUTTONUP:
\r
4158 case WM_MOUSEMOVE:
\r
4159 case WM_MOUSEWHEEL:
\r
4160 MouseEvent(hwnd, message, wParam, lParam);
\r
4163 JAWS_KB_NAVIGATION
\r
4167 JAWS_ALT_INTERCEPT
\r
4169 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4170 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4171 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4172 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4174 SendMessage(h, message, wParam, lParam);
\r
4175 } else if(lParam != KF_REPEAT) {
\r
4176 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4177 PopUpMoveDialog((char)wParam);
\r
4178 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4179 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4184 case WM_PALETTECHANGED:
\r
4185 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4187 HDC hdc = GetDC(hwndMain);
\r
4188 SelectPalette(hdc, hPal, TRUE);
\r
4189 nnew = RealizePalette(hdc);
\r
4191 paletteChanged = TRUE;
\r
4192 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4194 ReleaseDC(hwnd, hdc);
\r
4198 case WM_QUERYNEWPALETTE:
\r
4199 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4201 HDC hdc = GetDC(hwndMain);
\r
4202 paletteChanged = FALSE;
\r
4203 SelectPalette(hdc, hPal, FALSE);
\r
4204 nnew = RealizePalette(hdc);
\r
4206 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4208 ReleaseDC(hwnd, hdc);
\r
4213 case WM_COMMAND: /* message: command from application menu */
\r
4214 wmId = LOWORD(wParam);
\r
4215 wmEvent = HIWORD(wParam);
\r
4220 SAY("new game enter a move to play against the computer with white");
\r
4223 case IDM_NewGameFRC:
\r
4224 if( NewGameFRC() == 0 ) {
\r
4229 case IDM_NewVariant:
\r
4230 NewVariantPopup(hwnd);
\r
4233 case IDM_LoadGame:
\r
4234 LoadGameDialog(hwnd, "Load Game from File");
\r
4237 case IDM_LoadNextGame:
\r
4241 case IDM_LoadPrevGame:
\r
4245 case IDM_ReloadGame:
\r
4249 case IDM_LoadPosition:
\r
4250 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4251 Reset(FALSE, TRUE);
\r
4254 f = OpenFileDialog(hwnd, "rb", "",
\r
4255 appData.oldSaveStyle ? "pos" : "fen",
\r
4257 "Load Position from File", &number, fileTitle, NULL);
\r
4259 LoadPosition(f, number, fileTitle);
\r
4263 case IDM_LoadNextPosition:
\r
4264 ReloadPosition(1);
\r
4267 case IDM_LoadPrevPosition:
\r
4268 ReloadPosition(-1);
\r
4271 case IDM_ReloadPosition:
\r
4272 ReloadPosition(0);
\r
4275 case IDM_SaveGame:
\r
4276 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4277 f = OpenFileDialog(hwnd, "a", defName,
\r
4278 appData.oldSaveStyle ? "gam" : "pgn",
\r
4280 "Save Game to File", NULL, fileTitle, NULL);
\r
4282 SaveGame(f, 0, "");
\r
4286 case IDM_SavePosition:
\r
4287 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4288 f = OpenFileDialog(hwnd, "a", defName,
\r
4289 appData.oldSaveStyle ? "pos" : "fen",
\r
4291 "Save Position to File", NULL, fileTitle, NULL);
\r
4293 SavePosition(f, 0, "");
\r
4297 case IDM_SaveDiagram:
\r
4298 defName = "diagram";
\r
4299 f = OpenFileDialog(hwnd, "wb", defName,
\r
4302 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4308 case IDM_CopyGame:
\r
4309 CopyGameToClipboard();
\r
4312 case IDM_PasteGame:
\r
4313 PasteGameFromClipboard();
\r
4316 case IDM_CopyGameListToClipboard:
\r
4317 CopyGameListToClipboard();
\r
4320 /* [AS] Autodetect FEN or PGN data */
\r
4321 case IDM_PasteAny:
\r
4322 PasteGameOrFENFromClipboard();
\r
4325 /* [AS] Move history */
\r
4326 case IDM_ShowMoveHistory:
\r
4327 if( MoveHistoryIsUp() ) {
\r
4328 MoveHistoryPopDown();
\r
4331 MoveHistoryPopUp();
\r
4335 /* [AS] Eval graph */
\r
4336 case IDM_ShowEvalGraph:
\r
4337 if( EvalGraphIsUp() ) {
\r
4338 EvalGraphPopDown();
\r
4342 SetFocus(hwndMain);
\r
4346 /* [AS] Engine output */
\r
4347 case IDM_ShowEngineOutput:
\r
4348 if( EngineOutputIsUp() ) {
\r
4349 EngineOutputPopDown();
\r
4352 EngineOutputPopUp();
\r
4356 /* [AS] User adjudication */
\r
4357 case IDM_UserAdjudication_White:
\r
4358 UserAdjudicationEvent( +1 );
\r
4361 case IDM_UserAdjudication_Black:
\r
4362 UserAdjudicationEvent( -1 );
\r
4365 case IDM_UserAdjudication_Draw:
\r
4366 UserAdjudicationEvent( 0 );
\r
4369 /* [AS] Game list options dialog */
\r
4370 case IDM_GameListOptions:
\r
4371 GameListOptions();
\r
4378 case IDM_CopyPosition:
\r
4379 CopyFENToClipboard();
\r
4382 case IDM_PastePosition:
\r
4383 PasteFENFromClipboard();
\r
4386 case IDM_MailMove:
\r
4390 case IDM_ReloadCMailMsg:
\r
4391 Reset(TRUE, TRUE);
\r
4392 ReloadCmailMsgEvent(FALSE);
\r
4395 case IDM_Minimize:
\r
4396 ShowWindow(hwnd, SW_MINIMIZE);
\r
4403 case IDM_MachineWhite:
\r
4404 MachineWhiteEvent();
\r
4406 * refresh the tags dialog only if it's visible
\r
4408 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4410 tags = PGNTags(&gameInfo);
\r
4411 TagsPopUp(tags, CmailMsg());
\r
4414 SAY("computer starts playing white");
\r
4417 case IDM_MachineBlack:
\r
4418 MachineBlackEvent();
\r
4420 * refresh the tags dialog only if it's visible
\r
4422 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4424 tags = PGNTags(&gameInfo);
\r
4425 TagsPopUp(tags, CmailMsg());
\r
4428 SAY("computer starts playing black");
\r
4431 case IDM_TwoMachines:
\r
4432 TwoMachinesEvent();
\r
4434 * refresh the tags dialog only if it's visible
\r
4436 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4438 tags = PGNTags(&gameInfo);
\r
4439 TagsPopUp(tags, CmailMsg());
\r
4442 SAY("programs start playing each other");
\r
4445 case IDM_AnalysisMode:
\r
4446 if (!first.analysisSupport) {
\r
4447 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4448 DisplayError(buf, 0);
\r
4450 SAY("analyzing current position");
\r
4451 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4452 if (appData.icsActive) {
\r
4453 if (gameMode != IcsObserving) {
\r
4454 sprintf(buf, "You are not observing a game");
\r
4455 DisplayError(buf, 0);
\r
4456 /* secure check */
\r
4457 if (appData.icsEngineAnalyze) {
\r
4458 if (appData.debugMode)
\r
4459 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4460 ExitAnalyzeMode();
\r
4466 /* if enable, user want disable icsEngineAnalyze */
\r
4467 if (appData.icsEngineAnalyze) {
\r
4468 ExitAnalyzeMode();
\r
4472 appData.icsEngineAnalyze = TRUE;
\r
4473 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4476 if (!appData.showThinking) ToggleShowThinking();
\r
4477 AnalyzeModeEvent();
\r
4481 case IDM_AnalyzeFile:
\r
4482 if (!first.analysisSupport) {
\r
4483 char buf[MSG_SIZ];
\r
4484 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4485 DisplayError(buf, 0);
\r
4487 if (!appData.showThinking) ToggleShowThinking();
\r
4488 AnalyzeFileEvent();
\r
4489 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4490 AnalysisPeriodicEvent(1);
\r
4494 case IDM_IcsClient:
\r
4498 case IDM_EditGame:
\r
4503 case IDM_EditPosition:
\r
4504 EditPositionEvent();
\r
4505 SAY("to set up a position type a FEN");
\r
4508 case IDM_Training:
\r
4512 case IDM_ShowGameList:
\r
4513 ShowGameListProc();
\r
4516 case IDM_EditTags:
\r
4520 case IDM_EditComment:
\r
4521 if (commentUp && editComment) {
\r
4524 EditCommentEvent();
\r
4544 case IDM_CallFlag:
\r
4564 case IDM_StopObserving:
\r
4565 StopObservingEvent();
\r
4568 case IDM_StopExamining:
\r
4569 StopExaminingEvent();
\r
4572 case IDM_TypeInMove:
\r
4573 PopUpMoveDialog('\000');
\r
4576 case IDM_TypeInName:
\r
4577 PopUpNameDialog('\000');
\r
4580 case IDM_Backward:
\r
4582 SetFocus(hwndMain);
\r
4589 SetFocus(hwndMain);
\r
4594 SetFocus(hwndMain);
\r
4599 SetFocus(hwndMain);
\r
4606 case IDM_TruncateGame:
\r
4607 TruncateGameEvent();
\r
4614 case IDM_RetractMove:
\r
4615 RetractMoveEvent();
\r
4618 case IDM_FlipView:
\r
4619 flipView = !flipView;
\r
4620 DrawPosition(FALSE, NULL);
\r
4623 case IDM_FlipClock:
\r
4624 flipClock = !flipClock;
\r
4625 DisplayBothClocks();
\r
4626 DrawPosition(FALSE, NULL);
\r
4629 case IDM_MuteSounds:
\r
4630 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4631 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4632 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4635 case IDM_GeneralOptions:
\r
4636 GeneralOptionsPopup(hwnd);
\r
4637 DrawPosition(TRUE, NULL);
\r
4640 case IDM_BoardOptions:
\r
4641 BoardOptionsPopup(hwnd);
\r
4644 case IDM_EnginePlayOptions:
\r
4645 EnginePlayOptionsPopup(hwnd);
\r
4648 case IDM_Engine1Options:
\r
4649 EngineOptionsPopup(hwnd, &first);
\r
4652 case IDM_Engine2Options:
\r
4653 EngineOptionsPopup(hwnd, &second);
\r
4656 case IDM_OptionsUCI:
\r
4657 UciOptionsPopup(hwnd);
\r
4660 case IDM_IcsOptions:
\r
4661 IcsOptionsPopup(hwnd);
\r
4665 FontsOptionsPopup(hwnd);
\r
4669 SoundOptionsPopup(hwnd);
\r
4672 case IDM_CommPort:
\r
4673 CommPortOptionsPopup(hwnd);
\r
4676 case IDM_LoadOptions:
\r
4677 LoadOptionsPopup(hwnd);
\r
4680 case IDM_SaveOptions:
\r
4681 SaveOptionsPopup(hwnd);
\r
4684 case IDM_TimeControl:
\r
4685 TimeControlOptionsPopup(hwnd);
\r
4688 case IDM_SaveSettings:
\r
4689 SaveSettings(settingsFileName);
\r
4692 case IDM_SaveSettingsOnExit:
\r
4693 saveSettingsOnExit = !saveSettingsOnExit;
\r
4694 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4695 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4696 MF_CHECKED : MF_UNCHECKED));
\r
4707 case IDM_AboutGame:
\r
4712 appData.debugMode = !appData.debugMode;
\r
4713 if (appData.debugMode) {
\r
4714 char dir[MSG_SIZ];
\r
4715 GetCurrentDirectory(MSG_SIZ, dir);
\r
4716 SetCurrentDirectory(installDir);
\r
4717 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4718 SetCurrentDirectory(dir);
\r
4719 setbuf(debugFP, NULL);
\r
4726 case IDM_HELPCONTENTS:
\r
4727 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\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_HELPSEARCH:
\r
4736 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4737 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4738 MessageBox (GetFocus(),
\r
4739 "Unable to activate help",
\r
4740 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4744 case IDM_HELPHELP:
\r
4745 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4746 MessageBox (GetFocus(),
\r
4747 "Unable to activate help",
\r
4748 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4753 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4755 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4756 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4757 FreeProcInstance(lpProc);
\r
4760 case IDM_DirectCommand1:
\r
4761 AskQuestionEvent("Direct Command",
\r
4762 "Send to chess program:", "", "1");
\r
4764 case IDM_DirectCommand2:
\r
4765 AskQuestionEvent("Direct Command",
\r
4766 "Send to second chess program:", "", "2");
\r
4769 case EP_WhitePawn:
\r
4770 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4771 fromX = fromY = -1;
\r
4774 case EP_WhiteKnight:
\r
4775 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4776 fromX = fromY = -1;
\r
4779 case EP_WhiteBishop:
\r
4780 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4781 fromX = fromY = -1;
\r
4784 case EP_WhiteRook:
\r
4785 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4786 fromX = fromY = -1;
\r
4789 case EP_WhiteQueen:
\r
4790 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4791 fromX = fromY = -1;
\r
4794 case EP_WhiteFerz:
\r
4795 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4796 fromX = fromY = -1;
\r
4799 case EP_WhiteWazir:
\r
4800 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4801 fromX = fromY = -1;
\r
4804 case EP_WhiteAlfil:
\r
4805 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4806 fromX = fromY = -1;
\r
4809 case EP_WhiteCannon:
\r
4810 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4811 fromX = fromY = -1;
\r
4814 case EP_WhiteCardinal:
\r
4815 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4816 fromX = fromY = -1;
\r
4819 case EP_WhiteMarshall:
\r
4820 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4821 fromX = fromY = -1;
\r
4824 case EP_WhiteKing:
\r
4825 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4826 fromX = fromY = -1;
\r
4829 case EP_BlackPawn:
\r
4830 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4831 fromX = fromY = -1;
\r
4834 case EP_BlackKnight:
\r
4835 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4836 fromX = fromY = -1;
\r
4839 case EP_BlackBishop:
\r
4840 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4841 fromX = fromY = -1;
\r
4844 case EP_BlackRook:
\r
4845 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4846 fromX = fromY = -1;
\r
4849 case EP_BlackQueen:
\r
4850 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4851 fromX = fromY = -1;
\r
4854 case EP_BlackFerz:
\r
4855 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4856 fromX = fromY = -1;
\r
4859 case EP_BlackWazir:
\r
4860 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4861 fromX = fromY = -1;
\r
4864 case EP_BlackAlfil:
\r
4865 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4866 fromX = fromY = -1;
\r
4869 case EP_BlackCannon:
\r
4870 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4871 fromX = fromY = -1;
\r
4874 case EP_BlackCardinal:
\r
4875 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4876 fromX = fromY = -1;
\r
4879 case EP_BlackMarshall:
\r
4880 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4881 fromX = fromY = -1;
\r
4884 case EP_BlackKing:
\r
4885 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4886 fromX = fromY = -1;
\r
4889 case EP_EmptySquare:
\r
4890 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4891 fromX = fromY = -1;
\r
4894 case EP_ClearBoard:
\r
4895 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4896 fromX = fromY = -1;
\r
4900 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4901 fromX = fromY = -1;
\r
4905 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4906 fromX = fromY = -1;
\r
4910 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4911 fromX = fromY = -1;
\r
4915 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4916 fromX = fromY = -1;
\r
4920 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4921 fromX = fromY = -1;
\r
4925 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4926 fromX = fromY = -1;
\r
4930 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4931 fromX = fromY = -1;
\r
4935 DropMenuEvent(WhiteRook, fromX, fromY);
\r
4936 fromX = fromY = -1;
\r
4940 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
4941 fromX = fromY = -1;
\r
4945 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4951 case CLOCK_TIMER_ID:
\r
4952 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
4953 clockTimerEvent = 0;
\r
4954 DecrementClocks(); /* call into back end */
\r
4956 case LOAD_GAME_TIMER_ID:
\r
4957 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
4958 loadGameTimerEvent = 0;
\r
4959 AutoPlayGameLoop(); /* call into back end */
\r
4961 case ANALYSIS_TIMER_ID:
\r
4962 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
4963 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
4964 AnalysisPeriodicEvent(0);
\r
4966 KillTimer(hwnd, analysisTimerEvent);
\r
4967 analysisTimerEvent = 0;
\r
4970 case DELAYED_TIMER_ID:
\r
4971 KillTimer(hwnd, delayedTimerEvent);
\r
4972 delayedTimerEvent = 0;
\r
4973 delayedTimerCallback();
\r
4978 case WM_USER_Input:
\r
4979 InputEvent(hwnd, message, wParam, lParam);
\r
4982 /* [AS] Also move "attached" child windows */
\r
4983 case WM_WINDOWPOSCHANGING:
\r
4985 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
4986 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
4988 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
4989 /* Window is moving */
\r
4992 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
4993 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
4994 rcMain.right = wpMain.x + wpMain.width;
\r
4995 rcMain.top = wpMain.y;
\r
4996 rcMain.bottom = wpMain.y + wpMain.height;
\r
4998 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
4999 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5000 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5001 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5002 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5003 wpMain.x = lpwp->x;
\r
5004 wpMain.y = lpwp->y;
\r
5009 /* [AS] Snapping */
\r
5010 case WM_ENTERSIZEMOVE:
\r
5011 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5012 if (hwnd == hwndMain) {
\r
5013 doingSizing = TRUE;
\r
5016 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5020 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5021 if (hwnd == hwndMain) {
\r
5022 lastSizing = wParam;
\r
5027 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5028 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5030 case WM_EXITSIZEMOVE:
\r
5031 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5032 if (hwnd == hwndMain) {
\r
5034 doingSizing = FALSE;
\r
5035 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5036 GetClientRect(hwnd, &client);
\r
5037 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5039 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5041 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5044 case WM_DESTROY: /* message: window being destroyed */
\r
5045 PostQuitMessage(0);
\r
5049 if (hwnd == hwndMain) {
\r
5054 default: /* Passes it on if unprocessed */
\r
5055 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5060 /*---------------------------------------------------------------------------*\
\r
5062 * Misc utility routines
\r
5064 \*---------------------------------------------------------------------------*/
\r
5067 * Decent random number generator, at least not as bad as Windows
\r
5068 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5070 unsigned int randstate;
\r
5075 randstate = randstate * 1664525 + 1013904223;
\r
5076 return (int) randstate & 0x7fffffff;
\r
5080 mysrandom(unsigned int seed)
\r
5087 * returns TRUE if user selects a different color, FALSE otherwise
\r
5091 ChangeColor(HWND hwnd, COLORREF *which)
\r
5093 static BOOL firstTime = TRUE;
\r
5094 static DWORD customColors[16];
\r
5096 COLORREF newcolor;
\r
5101 /* Make initial colors in use available as custom colors */
\r
5102 /* Should we put the compiled-in defaults here instead? */
\r
5104 customColors[i++] = lightSquareColor & 0xffffff;
\r
5105 customColors[i++] = darkSquareColor & 0xffffff;
\r
5106 customColors[i++] = whitePieceColor & 0xffffff;
\r
5107 customColors[i++] = blackPieceColor & 0xffffff;
\r
5108 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5109 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5111 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5112 customColors[i++] = textAttribs[ccl].color;
\r
5114 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5115 firstTime = FALSE;
\r
5118 cc.lStructSize = sizeof(cc);
\r
5119 cc.hwndOwner = hwnd;
\r
5120 cc.hInstance = NULL;
\r
5121 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5122 cc.lpCustColors = (LPDWORD) customColors;
\r
5123 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5125 if (!ChooseColor(&cc)) return FALSE;
\r
5127 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5128 if (newcolor == *which) return FALSE;
\r
5129 *which = newcolor;
\r
5133 InitDrawingColors();
\r
5134 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5139 MyLoadSound(MySound *ms)
\r
5145 if (ms->data) free(ms->data);
\r
5148 switch (ms->name[0]) {
\r
5154 /* System sound from Control Panel. Don't preload here. */
\r
5158 if (ms->name[1] == NULLCHAR) {
\r
5159 /* "!" alone = silence */
\r
5162 /* Builtin wave resource. Error if not found. */
\r
5163 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5164 if (h == NULL) break;
\r
5165 ms->data = (void *)LoadResource(hInst, h);
\r
5166 if (h == NULL) break;
\r
5171 /* .wav file. Error if not found. */
\r
5172 f = fopen(ms->name, "rb");
\r
5173 if (f == NULL) break;
\r
5174 if (fstat(fileno(f), &st) < 0) break;
\r
5175 ms->data = malloc(st.st_size);
\r
5176 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5182 char buf[MSG_SIZ];
\r
5183 sprintf(buf, "Error loading sound %s", ms->name);
\r
5184 DisplayError(buf, GetLastError());
\r
5190 MyPlaySound(MySound *ms)
\r
5192 BOOLEAN ok = FALSE;
\r
5194 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5195 switch (ms->name[0]) {
\r
5197 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5202 /* System sound from Control Panel (deprecated feature).
\r
5203 "$" alone or an unset sound name gets default beep (still in use). */
\r
5204 if (ms->name[1]) {
\r
5205 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5207 if (!ok) ok = MessageBeep(MB_OK);
\r
5210 /* Builtin wave resource, or "!" alone for silence */
\r
5211 if (ms->name[1]) {
\r
5212 if (ms->data == NULL) return FALSE;
\r
5213 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5219 /* .wav file. Error if not found. */
\r
5220 if (ms->data == NULL) return FALSE;
\r
5221 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5224 /* Don't print an error: this can happen innocently if the sound driver
\r
5225 is busy; for instance, if another instance of WinBoard is playing
\r
5226 a sound at about the same time. */
\r
5232 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5235 OPENFILENAME *ofn;
\r
5236 static UINT *number; /* gross that this is static */
\r
5238 switch (message) {
\r
5239 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5240 /* Center the dialog over the application window */
\r
5241 ofn = (OPENFILENAME *) lParam;
\r
5242 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5243 number = (UINT *) ofn->lCustData;
\r
5244 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5248 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5249 return FALSE; /* Allow for further processing */
\r
5252 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5253 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5255 return FALSE; /* Allow for further processing */
\r
5261 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5263 static UINT *number;
\r
5264 OPENFILENAME *ofname;
\r
5267 case WM_INITDIALOG:
\r
5268 ofname = (OPENFILENAME *)lParam;
\r
5269 number = (UINT *)(ofname->lCustData);
\r
5272 ofnot = (OFNOTIFY *)lParam;
\r
5273 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5274 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5283 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5284 char *nameFilt, char *dlgTitle, UINT *number,
\r
5285 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5287 OPENFILENAME openFileName;
\r
5288 char buf1[MSG_SIZ];
\r
5291 if (fileName == NULL) fileName = buf1;
\r
5292 if (defName == NULL) {
\r
5293 strcpy(fileName, "*.");
\r
5294 strcat(fileName, defExt);
\r
5296 strcpy(fileName, defName);
\r
5298 if (fileTitle) strcpy(fileTitle, "");
\r
5299 if (number) *number = 0;
\r
5301 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5302 openFileName.hwndOwner = hwnd;
\r
5303 openFileName.hInstance = (HANDLE) hInst;
\r
5304 openFileName.lpstrFilter = nameFilt;
\r
5305 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5306 openFileName.nMaxCustFilter = 0L;
\r
5307 openFileName.nFilterIndex = 1L;
\r
5308 openFileName.lpstrFile = fileName;
\r
5309 openFileName.nMaxFile = MSG_SIZ;
\r
5310 openFileName.lpstrFileTitle = fileTitle;
\r
5311 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5312 openFileName.lpstrInitialDir = NULL;
\r
5313 openFileName.lpstrTitle = dlgTitle;
\r
5314 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5315 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5316 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5317 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5318 openFileName.nFileOffset = 0;
\r
5319 openFileName.nFileExtension = 0;
\r
5320 openFileName.lpstrDefExt = defExt;
\r
5321 openFileName.lCustData = (LONG) number;
\r
5322 openFileName.lpfnHook = oldDialog ?
\r
5323 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5324 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5326 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5327 GetOpenFileName(&openFileName)) {
\r
5328 /* open the file */
\r
5329 f = fopen(openFileName.lpstrFile, write);
\r
5331 MessageBox(hwnd, "File open failed", NULL,
\r
5332 MB_OK|MB_ICONEXCLAMATION);
\r
5336 int err = CommDlgExtendedError();
\r
5337 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5346 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5348 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5351 * Get the first pop-up menu in the menu template. This is the
\r
5352 * menu that TrackPopupMenu displays.
\r
5354 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5356 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5359 * TrackPopup uses screen coordinates, so convert the
\r
5360 * coordinates of the mouse click to screen coordinates.
\r
5362 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5364 /* Draw and track the floating pop-up menu. */
\r
5365 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5366 pt.x, pt.y, 0, hwnd, NULL);
\r
5368 /* Destroy the menu.*/
\r
5369 DestroyMenu(hmenu);
\r
5374 int sizeX, sizeY, newSizeX, newSizeY;
\r
5376 } ResizeEditPlusButtonsClosure;
\r
5379 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5381 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5385 if (hChild == cl->hText) return TRUE;
\r
5386 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5387 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5388 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5389 ScreenToClient(cl->hDlg, &pt);
\r
5390 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5391 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5395 /* Resize a dialog that has a (rich) edit field filling most of
\r
5396 the top, with a row of buttons below */
\r
5398 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5401 int newTextHeight, newTextWidth;
\r
5402 ResizeEditPlusButtonsClosure cl;
\r
5404 /*if (IsIconic(hDlg)) return;*/
\r
5405 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5407 cl.hdwp = BeginDeferWindowPos(8);
\r
5409 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5410 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5411 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5412 if (newTextHeight < 0) {
\r
5413 newSizeY += -newTextHeight;
\r
5414 newTextHeight = 0;
\r
5416 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5417 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5423 cl.newSizeX = newSizeX;
\r
5424 cl.newSizeY = newSizeY;
\r
5425 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5427 EndDeferWindowPos(cl.hdwp);
\r
5430 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5432 RECT rChild, rParent;
\r
5433 int wChild, hChild, wParent, hParent;
\r
5434 int wScreen, hScreen, xNew, yNew;
\r
5437 /* Get the Height and Width of the child window */
\r
5438 GetWindowRect (hwndChild, &rChild);
\r
5439 wChild = rChild.right - rChild.left;
\r
5440 hChild = rChild.bottom - rChild.top;
\r
5442 /* Get the Height and Width of the parent window */
\r
5443 GetWindowRect (hwndParent, &rParent);
\r
5444 wParent = rParent.right - rParent.left;
\r
5445 hParent = rParent.bottom - rParent.top;
\r
5447 /* Get the display limits */
\r
5448 hdc = GetDC (hwndChild);
\r
5449 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5450 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5451 ReleaseDC(hwndChild, hdc);
\r
5453 /* Calculate new X position, then adjust for screen */
\r
5454 xNew = rParent.left + ((wParent - wChild) /2);
\r
5457 } else if ((xNew+wChild) > wScreen) {
\r
5458 xNew = wScreen - wChild;
\r
5461 /* Calculate new Y position, then adjust for screen */
\r
5463 yNew = rParent.top + ((hParent - hChild) /2);
\r
5466 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5471 } else if ((yNew+hChild) > hScreen) {
\r
5472 yNew = hScreen - hChild;
\r
5475 /* Set it, and return */
\r
5476 return SetWindowPos (hwndChild, NULL,
\r
5477 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5480 /* Center one window over another */
\r
5481 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5483 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5486 /*---------------------------------------------------------------------------*\
\r
5488 * Startup Dialog functions
\r
5490 \*---------------------------------------------------------------------------*/
\r
5492 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5494 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5496 while (*cd != NULL) {
\r
5497 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5503 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5505 char buf1[MAX_ARG_LEN];
\r
5508 if (str[0] == '@') {
\r
5509 FILE* f = fopen(str + 1, "r");
\r
5511 DisplayFatalError(str + 1, errno, 2);
\r
5514 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5516 buf1[len] = NULLCHAR;
\r
5520 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5523 char buf[MSG_SIZ];
\r
5524 char *end = strchr(str, '\n');
\r
5525 if (end == NULL) return;
\r
5526 memcpy(buf, str, end - str);
\r
5527 buf[end - str] = NULLCHAR;
\r
5528 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5534 SetStartupDialogEnables(HWND hDlg)
\r
5536 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5537 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5538 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5539 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5540 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5541 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5542 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5543 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5544 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5545 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5546 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5547 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5548 IsDlgButtonChecked(hDlg, OPT_View));
\r
5552 QuoteForFilename(char *filename)
\r
5554 int dquote, space;
\r
5555 dquote = strchr(filename, '"') != NULL;
\r
5556 space = strchr(filename, ' ') != NULL;
\r
5557 if (dquote || space) {
\r
5569 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5571 char buf[MSG_SIZ];
\r
5574 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5575 q = QuoteForFilename(nthcp);
\r
5576 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5577 if (*nthdir != NULLCHAR) {
\r
5578 q = QuoteForFilename(nthdir);
\r
5579 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5581 if (*nthcp == NULLCHAR) {
\r
5582 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5583 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5584 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5585 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5590 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5592 char buf[MSG_SIZ];
\r
5596 switch (message) {
\r
5597 case WM_INITDIALOG:
\r
5598 /* Center the dialog */
\r
5599 CenterWindow (hDlg, GetDesktopWindow());
\r
5600 /* Initialize the dialog items */
\r
5601 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5602 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5603 firstChessProgramNames);
\r
5604 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5605 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5606 secondChessProgramNames);
\r
5607 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5608 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5609 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5610 if (*appData.icsHelper != NULLCHAR) {
\r
5611 char *q = QuoteForFilename(appData.icsHelper);
\r
5612 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5614 if (*appData.icsHost == NULLCHAR) {
\r
5615 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5616 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5617 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5618 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5619 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5622 if (appData.icsActive) {
\r
5623 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5625 else if (appData.noChessProgram) {
\r
5626 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5629 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5632 SetStartupDialogEnables(hDlg);
\r
5636 switch (LOWORD(wParam)) {
\r
5638 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5639 strcpy(buf, "/fcp=");
\r
5640 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5642 ParseArgs(StringGet, &p);
\r
5643 strcpy(buf, "/scp=");
\r
5644 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5646 ParseArgs(StringGet, &p);
\r
5647 appData.noChessProgram = FALSE;
\r
5648 appData.icsActive = FALSE;
\r
5649 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5650 strcpy(buf, "/ics /icshost=");
\r
5651 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5653 ParseArgs(StringGet, &p);
\r
5654 if (appData.zippyPlay) {
\r
5655 strcpy(buf, "/fcp=");
\r
5656 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5658 ParseArgs(StringGet, &p);
\r
5660 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5661 appData.noChessProgram = TRUE;
\r
5662 appData.icsActive = FALSE;
\r
5664 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5665 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5668 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5669 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5671 ParseArgs(StringGet, &p);
\r
5673 EndDialog(hDlg, TRUE);
\r
5680 case IDM_HELPCONTENTS:
\r
5681 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5682 MessageBox (GetFocus(),
\r
5683 "Unable to activate help",
\r
5684 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5689 SetStartupDialogEnables(hDlg);
\r
5697 /*---------------------------------------------------------------------------*\
\r
5699 * About box dialog functions
\r
5701 \*---------------------------------------------------------------------------*/
\r
5703 /* Process messages for "About" dialog box */
\r
5705 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5707 switch (message) {
\r
5708 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5709 /* Center the dialog over the application window */
\r
5710 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5711 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5715 case WM_COMMAND: /* message: received a command */
\r
5716 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5717 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5718 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5726 /*---------------------------------------------------------------------------*\
\r
5728 * Comment Dialog functions
\r
5730 \*---------------------------------------------------------------------------*/
\r
5733 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5735 static HANDLE hwndText = NULL;
\r
5736 int len, newSizeX, newSizeY, flags;
\r
5737 static int sizeX, sizeY;
\r
5742 switch (message) {
\r
5743 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5744 /* Initialize the dialog items */
\r
5745 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5746 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5747 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5748 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5749 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5750 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5751 SetWindowText(hDlg, commentTitle);
\r
5752 if (editComment) {
\r
5753 SetFocus(hwndText);
\r
5755 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5757 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5758 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5759 MAKELPARAM(FALSE, 0));
\r
5760 /* Size and position the dialog */
\r
5761 if (!commentDialog) {
\r
5762 commentDialog = hDlg;
\r
5763 flags = SWP_NOZORDER;
\r
5764 GetClientRect(hDlg, &rect);
\r
5765 sizeX = rect.right;
\r
5766 sizeY = rect.bottom;
\r
5767 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5768 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5769 WINDOWPLACEMENT wp;
\r
5770 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5771 wp.length = sizeof(WINDOWPLACEMENT);
\r
5773 wp.showCmd = SW_SHOW;
\r
5774 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5775 wp.rcNormalPosition.left = wpComment.x;
\r
5776 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5777 wp.rcNormalPosition.top = wpComment.y;
\r
5778 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5779 SetWindowPlacement(hDlg, &wp);
\r
5781 GetClientRect(hDlg, &rect);
\r
5782 newSizeX = rect.right;
\r
5783 newSizeY = rect.bottom;
\r
5784 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5785 newSizeX, newSizeY);
\r
5792 case WM_COMMAND: /* message: received a command */
\r
5793 switch (LOWORD(wParam)) {
\r
5795 if (editComment) {
\r
5797 /* Read changed options from the dialog box */
\r
5798 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5799 len = GetWindowTextLength(hwndText);
\r
5800 str = (char *) malloc(len + 1);
\r
5801 GetWindowText(hwndText, str, len + 1);
\r
5810 ReplaceComment(commentIndex, str);
\r
5817 case OPT_CancelComment:
\r
5821 case OPT_ClearComment:
\r
5822 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5825 case OPT_EditComment:
\r
5826 EditCommentEvent();
\r
5835 newSizeX = LOWORD(lParam);
\r
5836 newSizeY = HIWORD(lParam);
\r
5837 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5842 case WM_GETMINMAXINFO:
\r
5843 /* Prevent resizing window too small */
\r
5844 mmi = (MINMAXINFO *) lParam;
\r
5845 mmi->ptMinTrackSize.x = 100;
\r
5846 mmi->ptMinTrackSize.y = 100;
\r
5853 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5858 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5860 if (str == NULL) str = "";
\r
5861 p = (char *) malloc(2 * strlen(str) + 2);
\r
5864 if (*str == '\n') *q++ = '\r';
\r
5868 if (commentText != NULL) free(commentText);
\r
5870 commentIndex = index;
\r
5871 commentTitle = title;
\r
5873 editComment = edit;
\r
5875 if (commentDialog) {
\r
5876 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5877 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5879 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5880 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5881 hwndMain, (DLGPROC)lpProc);
\r
5882 FreeProcInstance(lpProc);
\r
5888 /*---------------------------------------------------------------------------*\
\r
5890 * Type-in move dialog functions
\r
5892 \*---------------------------------------------------------------------------*/
\r
5895 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5897 char move[MSG_SIZ];
\r
5899 ChessMove moveType;
\r
5900 int fromX, fromY, toX, toY;
\r
5903 switch (message) {
\r
5904 case WM_INITDIALOG:
\r
5905 move[0] = (char) lParam;
\r
5906 move[1] = NULLCHAR;
\r
5907 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5908 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5909 SetWindowText(hInput, move);
\r
5911 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5915 switch (LOWORD(wParam)) {
\r
5917 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5918 { int n; Board board;
\r
5920 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5921 EditPositionPasteFEN(move);
\r
5922 EndDialog(hDlg, TRUE);
\r
5925 // [HGM] movenum: allow move number to be typed in any mode
\r
5926 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5928 EndDialog(hDlg, TRUE);
\r
5932 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5933 gameMode != Training) {
\r
5934 DisplayMoveError("Displayed move is not current");
\r
5936 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
5937 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5938 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
5939 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
5940 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5941 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
5942 if (gameMode != Training)
\r
5943 forwardMostMove = currentMove;
\r
5944 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
5946 DisplayMoveError("Could not parse move");
\r
5949 EndDialog(hDlg, TRUE);
\r
5952 EndDialog(hDlg, FALSE);
\r
5963 PopUpMoveDialog(char firstchar)
\r
5967 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
5968 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
5969 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
5970 gameMode == EditPosition || gameMode == IcsExamining ||
\r
5971 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
5972 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
5973 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
5974 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
5975 gameMode == Training) {
\r
5976 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
5977 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
5978 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
5979 FreeProcInstance(lpProc);
\r
5983 /*---------------------------------------------------------------------------*\
\r
5985 * Type-in name dialog functions
\r
5987 \*---------------------------------------------------------------------------*/
\r
5990 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5992 char move[MSG_SIZ];
\r
5995 switch (message) {
\r
5996 case WM_INITDIALOG:
\r
5997 move[0] = (char) lParam;
\r
5998 move[1] = NULLCHAR;
\r
5999 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6000 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6001 SetWindowText(hInput, move);
\r
6003 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6007 switch (LOWORD(wParam)) {
\r
6009 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6010 appData.userName = strdup(move);
\r
6013 EndDialog(hDlg, TRUE);
\r
6016 EndDialog(hDlg, FALSE);
\r
6027 PopUpNameDialog(char firstchar)
\r
6031 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6032 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6033 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6034 FreeProcInstance(lpProc);
\r
6037 /*---------------------------------------------------------------------------*\
\r
6041 \*---------------------------------------------------------------------------*/
\r
6043 /* Nonmodal error box */
\r
6044 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6045 WPARAM wParam, LPARAM lParam);
\r
6048 ErrorPopUp(char *title, char *content)
\r
6052 BOOLEAN modal = hwndMain == NULL;
\r
6070 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6071 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6074 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6076 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6077 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6078 hwndMain, (DLGPROC)lpProc);
\r
6079 FreeProcInstance(lpProc);
\r
6086 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6087 if (errorDialog == NULL) return;
\r
6088 DestroyWindow(errorDialog);
\r
6089 errorDialog = NULL;
\r
6090 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6094 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6099 switch (message) {
\r
6100 case WM_INITDIALOG:
\r
6101 GetWindowRect(hDlg, &rChild);
\r
6104 SetWindowPos(hDlg, NULL, rChild.left,
\r
6105 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6106 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6110 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6111 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6112 and it doesn't work when you resize the dialog.
\r
6113 For now, just give it a default position.
\r
6115 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6117 errorDialog = hDlg;
\r
6118 SetWindowText(hDlg, errorTitle);
\r
6119 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6120 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6124 switch (LOWORD(wParam)) {
\r
6127 if (errorDialog == hDlg) errorDialog = NULL;
\r
6128 DestroyWindow(hDlg);
\r
6140 HWND gothicDialog = NULL;
\r
6143 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6147 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6149 switch (message) {
\r
6150 case WM_INITDIALOG:
\r
6151 GetWindowRect(hDlg, &rChild);
\r
6153 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6157 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6158 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6159 and it doesn't work when you resize the dialog.
\r
6160 For now, just give it a default position.
\r
6162 gothicDialog = hDlg;
\r
6163 SetWindowText(hDlg, errorTitle);
\r
6164 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6165 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6169 switch (LOWORD(wParam)) {
\r
6172 if (errorDialog == hDlg) errorDialog = NULL;
\r
6173 DestroyWindow(hDlg);
\r
6185 GothicPopUp(char *title, VariantClass variant)
\r
6188 static char *lastTitle;
\r
6190 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6191 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6193 if(lastTitle != title && gothicDialog != NULL) {
\r
6194 DestroyWindow(gothicDialog);
\r
6195 gothicDialog = NULL;
\r
6197 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6198 title = lastTitle;
\r
6199 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6200 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6201 hwndMain, (DLGPROC)lpProc);
\r
6202 FreeProcInstance(lpProc);
\r
6207 /*---------------------------------------------------------------------------*\
\r
6209 * Ics Interaction console functions
\r
6211 \*---------------------------------------------------------------------------*/
\r
6213 #define HISTORY_SIZE 64
\r
6214 static char *history[HISTORY_SIZE];
\r
6215 int histIn = 0, histP = 0;
\r
6218 SaveInHistory(char *cmd)
\r
6220 if (history[histIn] != NULL) {
\r
6221 free(history[histIn]);
\r
6222 history[histIn] = NULL;
\r
6224 if (*cmd == NULLCHAR) return;
\r
6225 history[histIn] = StrSave(cmd);
\r
6226 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6227 if (history[histIn] != NULL) {
\r
6228 free(history[histIn]);
\r
6229 history[histIn] = NULL;
\r
6235 PrevInHistory(char *cmd)
\r
6238 if (histP == histIn) {
\r
6239 if (history[histIn] != NULL) free(history[histIn]);
\r
6240 history[histIn] = StrSave(cmd);
\r
6242 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6243 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6245 return history[histP];
\r
6251 if (histP == histIn) return NULL;
\r
6252 histP = (histP + 1) % HISTORY_SIZE;
\r
6253 return history[histP];
\r
6257 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6261 hmenu = LoadMenu(hInst, "TextMenu");
\r
6262 h = GetSubMenu(hmenu, 0);
\r
6264 if (strcmp(e->item, "-") == 0) {
\r
6265 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6267 if (e->item[0] == '|') {
\r
6268 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
\r
6269 IDM_CommandX + i, &e->item[1]);
\r
6271 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
\r
6280 WNDPROC consoleTextWindowProc;
\r
6283 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6285 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6286 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6290 SetWindowText(hInput, command);
\r
6292 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6294 sel.cpMin = 999999;
\r
6295 sel.cpMax = 999999;
\r
6296 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6301 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6302 if (sel.cpMin == sel.cpMax) {
\r
6303 /* Expand to surrounding word */
\r
6306 tr.chrg.cpMax = sel.cpMin;
\r
6307 tr.chrg.cpMin = --sel.cpMin;
\r
6308 if (sel.cpMin < 0) break;
\r
6309 tr.lpstrText = name;
\r
6310 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6311 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6315 tr.chrg.cpMin = sel.cpMax;
\r
6316 tr.chrg.cpMax = ++sel.cpMax;
\r
6317 tr.lpstrText = name;
\r
6318 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6319 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6322 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6323 MessageBeep(MB_ICONEXCLAMATION);
\r
6327 tr.lpstrText = name;
\r
6328 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6330 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6331 MessageBeep(MB_ICONEXCLAMATION);
\r
6334 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6337 sprintf(buf, "%s %s", command, name);
\r
6338 SetWindowText(hInput, buf);
\r
6339 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6341 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6342 SetWindowText(hInput, buf);
\r
6343 sel.cpMin = 999999;
\r
6344 sel.cpMax = 999999;
\r
6345 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6351 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6356 switch (message) {
\r
6358 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6361 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6364 sel.cpMin = 999999;
\r
6365 sel.cpMax = 999999;
\r
6366 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6367 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6372 if(wParam != '\022') {
\r
6373 if (wParam == '\t') {
\r
6374 if (GetKeyState(VK_SHIFT) < 0) {
\r
6376 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6377 if (buttonDesc[0].hwnd) {
\r
6378 SetFocus(buttonDesc[0].hwnd);
\r
6380 SetFocus(hwndMain);
\r
6384 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6387 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6388 JAWS_DELETE( SetFocus(hInput); )
\r
6389 SendMessage(hInput, message, wParam, lParam);
\r
6392 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6393 case WM_RBUTTONUP:
\r
6394 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6395 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6396 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6399 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6400 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6401 if (sel.cpMin == sel.cpMax) {
\r
6402 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6403 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6405 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6406 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6408 pt.x = LOWORD(lParam);
\r
6409 pt.y = HIWORD(lParam);
\r
6410 MenuPopup(hwnd, pt, hmenu, -1);
\r
6414 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6416 return SendMessage(hInput, message, wParam, lParam);
\r
6417 case WM_MBUTTONDOWN:
\r
6418 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6419 case WM_RBUTTONDOWN:
\r
6420 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6421 /* Move selection here if it was empty */
\r
6423 pt.x = LOWORD(lParam);
\r
6424 pt.y = HIWORD(lParam);
\r
6425 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6426 if (sel.cpMin == sel.cpMax) {
\r
6427 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6428 sel.cpMax = sel.cpMin;
\r
6429 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6431 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6435 switch (LOWORD(wParam)) {
\r
6436 case IDM_QuickPaste:
\r
6438 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6439 if (sel.cpMin == sel.cpMax) {
\r
6440 MessageBeep(MB_ICONEXCLAMATION);
\r
6443 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6444 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6445 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6450 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6453 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6456 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6460 int i = LOWORD(wParam) - IDM_CommandX;
\r
6461 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6462 icsTextMenuEntry[i].command != NULL) {
\r
6463 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6464 icsTextMenuEntry[i].getname,
\r
6465 icsTextMenuEntry[i].immediate);
\r
6473 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6476 WNDPROC consoleInputWindowProc;
\r
6479 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6481 char buf[MSG_SIZ];
\r
6483 static BOOL sendNextChar = FALSE;
\r
6484 static BOOL quoteNextChar = FALSE;
\r
6485 InputSource *is = consoleInputSource;
\r
6489 switch (message) {
\r
6491 if (!appData.localLineEditing || sendNextChar) {
\r
6492 is->buf[0] = (CHAR) wParam;
\r
6494 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6495 sendNextChar = FALSE;
\r
6498 if (quoteNextChar) {
\r
6499 buf[0] = (char) wParam;
\r
6500 buf[1] = NULLCHAR;
\r
6501 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6502 quoteNextChar = FALSE;
\r
6506 case '\r': /* Enter key */
\r
6507 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6508 if (consoleEcho) SaveInHistory(is->buf);
\r
6509 is->buf[is->count++] = '\n';
\r
6510 is->buf[is->count] = NULLCHAR;
\r
6511 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6512 if (consoleEcho) {
\r
6513 ConsoleOutput(is->buf, is->count, TRUE);
\r
6514 } else if (appData.localLineEditing) {
\r
6515 ConsoleOutput("\n", 1, TRUE);
\r
6518 case '\033': /* Escape key */
\r
6519 SetWindowText(hwnd, "");
\r
6520 cf.cbSize = sizeof(CHARFORMAT);
\r
6521 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6522 if (consoleEcho) {
\r
6523 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6525 cf.crTextColor = COLOR_ECHOOFF;
\r
6527 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6528 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6530 case '\t': /* Tab key */
\r
6531 if (GetKeyState(VK_SHIFT) < 0) {
\r
6533 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6536 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6537 if (buttonDesc[0].hwnd) {
\r
6538 SetFocus(buttonDesc[0].hwnd);
\r
6540 SetFocus(hwndMain);
\r
6544 case '\023': /* Ctrl+S */
\r
6545 sendNextChar = TRUE;
\r
6547 case '\021': /* Ctrl+Q */
\r
6548 quoteNextChar = TRUE;
\r
6558 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6559 p = PrevInHistory(buf);
\r
6561 SetWindowText(hwnd, p);
\r
6562 sel.cpMin = 999999;
\r
6563 sel.cpMax = 999999;
\r
6564 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6569 p = NextInHistory();
\r
6571 SetWindowText(hwnd, p);
\r
6572 sel.cpMin = 999999;
\r
6573 sel.cpMax = 999999;
\r
6574 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6580 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6584 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6588 case WM_MBUTTONDOWN:
\r
6589 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6590 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6592 case WM_RBUTTONUP:
\r
6593 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6594 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6595 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6599 hmenu = LoadMenu(hInst, "InputMenu");
\r
6600 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6601 if (sel.cpMin == sel.cpMax) {
\r
6602 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6603 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6605 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6606 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6608 pt.x = LOWORD(lParam);
\r
6609 pt.y = HIWORD(lParam);
\r
6610 MenuPopup(hwnd, pt, hmenu, -1);
\r
6614 switch (LOWORD(wParam)) {
\r
6616 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6618 case IDM_SelectAll:
\r
6620 sel.cpMax = -1; /*999999?*/
\r
6621 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6624 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6627 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6630 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6635 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6638 #define CO_MAX 100000
\r
6639 #define CO_TRIM 1000
\r
6642 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6644 static SnapData sd;
\r
6645 HWND hText, hInput;
\r
6647 static int sizeX, sizeY;
\r
6648 int newSizeX, newSizeY;
\r
6652 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6653 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6655 switch (message) {
\r
6657 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6659 ENLINK *pLink = (ENLINK*)lParam;
\r
6660 if (pLink->msg == WM_LBUTTONUP)
\r
6664 tr.chrg = pLink->chrg;
\r
6665 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6666 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6667 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6668 free(tr.lpstrText);
\r
6672 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6673 hwndConsole = hDlg;
\r
6675 consoleTextWindowProc = (WNDPROC)
\r
6676 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6677 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6678 consoleInputWindowProc = (WNDPROC)
\r
6679 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6680 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6681 Colorize(ColorNormal, TRUE);
\r
6682 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6683 ChangedConsoleFont();
\r
6684 GetClientRect(hDlg, &rect);
\r
6685 sizeX = rect.right;
\r
6686 sizeY = rect.bottom;
\r
6687 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6688 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6689 WINDOWPLACEMENT wp;
\r
6690 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6691 wp.length = sizeof(WINDOWPLACEMENT);
\r
6693 wp.showCmd = SW_SHOW;
\r
6694 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6695 wp.rcNormalPosition.left = wpConsole.x;
\r
6696 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6697 wp.rcNormalPosition.top = wpConsole.y;
\r
6698 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6699 SetWindowPlacement(hDlg, &wp);
\r
6702 // [HGM] Chessknight's change 2004-07-13
\r
6703 else { /* Determine Defaults */
\r
6704 WINDOWPLACEMENT wp;
\r
6705 wpConsole.x = wpMain.width + 1;
\r
6706 wpConsole.y = wpMain.y;
\r
6707 wpConsole.width = screenWidth - wpMain.width;
\r
6708 wpConsole.height = wpMain.height;
\r
6709 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6710 wp.length = sizeof(WINDOWPLACEMENT);
\r
6712 wp.showCmd = SW_SHOW;
\r
6713 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6714 wp.rcNormalPosition.left = wpConsole.x;
\r
6715 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6716 wp.rcNormalPosition.top = wpConsole.y;
\r
6717 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6718 SetWindowPlacement(hDlg, &wp);
\r
6721 // Allow hText to highlight URLs and send notifications on them
\r
6722 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6723 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6724 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6725 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6739 if (IsIconic(hDlg)) break;
\r
6740 newSizeX = LOWORD(lParam);
\r
6741 newSizeY = HIWORD(lParam);
\r
6742 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6743 RECT rectText, rectInput;
\r
6745 int newTextHeight, newTextWidth;
\r
6746 GetWindowRect(hText, &rectText);
\r
6747 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6748 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6749 if (newTextHeight < 0) {
\r
6750 newSizeY += -newTextHeight;
\r
6751 newTextHeight = 0;
\r
6753 SetWindowPos(hText, NULL, 0, 0,
\r
6754 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6755 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6756 pt.x = rectInput.left;
\r
6757 pt.y = rectInput.top + newSizeY - sizeY;
\r
6758 ScreenToClient(hDlg, &pt);
\r
6759 SetWindowPos(hInput, NULL,
\r
6760 pt.x, pt.y, /* needs client coords */
\r
6761 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6762 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6768 case WM_GETMINMAXINFO:
\r
6769 /* Prevent resizing window too small */
\r
6770 mmi = (MINMAXINFO *) lParam;
\r
6771 mmi->ptMinTrackSize.x = 100;
\r
6772 mmi->ptMinTrackSize.y = 100;
\r
6775 /* [AS] Snapping */
\r
6776 case WM_ENTERSIZEMOVE:
\r
6777 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6780 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6783 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6785 case WM_EXITSIZEMOVE:
\r
6786 UpdateICSWidth(hText);
\r
6787 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6790 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6798 if (hwndConsole) return;
\r
6799 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6800 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6805 ConsoleOutput(char* data, int length, int forceVisible)
\r
6810 char buf[CO_MAX+1];
\r
6813 static int delayLF = 0;
\r
6814 CHARRANGE savesel, sel;
\r
6816 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6824 while (length--) {
\r
6832 } else if (*p == '\007') {
\r
6833 MyPlaySound(&sounds[(int)SoundBell]);
\r
6840 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6841 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6842 /* Save current selection */
\r
6843 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6844 exlen = GetWindowTextLength(hText);
\r
6845 /* Find out whether current end of text is visible */
\r
6846 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6847 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6848 /* Trim existing text if it's too long */
\r
6849 if (exlen + (q - buf) > CO_MAX) {
\r
6850 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6853 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6854 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6856 savesel.cpMin -= trim;
\r
6857 savesel.cpMax -= trim;
\r
6858 if (exlen < 0) exlen = 0;
\r
6859 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6860 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6862 /* Append the new text */
\r
6863 sel.cpMin = exlen;
\r
6864 sel.cpMax = exlen;
\r
6865 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6866 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6867 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6868 if (forceVisible || exlen == 0 ||
\r
6869 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6870 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6871 /* Scroll to make new end of text visible if old end of text
\r
6872 was visible or new text is an echo of user typein */
\r
6873 sel.cpMin = 9999999;
\r
6874 sel.cpMax = 9999999;
\r
6875 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6876 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6877 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6878 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6880 if (savesel.cpMax == exlen || forceVisible) {
\r
6881 /* Move insert point to new end of text if it was at the old
\r
6882 end of text or if the new text is an echo of user typein */
\r
6883 sel.cpMin = 9999999;
\r
6884 sel.cpMax = 9999999;
\r
6885 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6887 /* Restore previous selection */
\r
6888 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6890 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6897 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6901 COLORREF oldFg, oldBg;
\r
6905 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6907 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6908 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6909 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6912 rect.right = x + squareSize;
\r
6914 rect.bottom = y + squareSize;
\r
6917 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6918 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6919 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6920 &rect, str, strlen(str), NULL);
\r
6922 (void) SetTextColor(hdc, oldFg);
\r
6923 (void) SetBkColor(hdc, oldBg);
\r
6924 (void) SelectObject(hdc, oldFont);
\r
6928 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6929 RECT *rect, char *color, char *flagFell)
\r
6933 COLORREF oldFg, oldBg;
\r
6936 if (appData.clockMode) {
\r
6938 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
6940 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
6947 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6948 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6950 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
6951 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
6953 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6957 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6958 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
6959 rect, str, strlen(str), NULL);
\r
6960 if(logoHeight > 0 && appData.clockMode) {
\r
6962 sprintf(buf, "%s %s", buf+7, flagFell);
\r
6963 r.top = rect->top + logoHeight/2;
\r
6964 r.left = rect->left;
\r
6965 r.right = rect->right;
\r
6966 r.bottom = rect->bottom;
\r
6967 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6968 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
6969 &r, str, strlen(str), NULL);
\r
6971 (void) SetTextColor(hdc, oldFg);
\r
6972 (void) SetBkColor(hdc, oldBg);
\r
6973 (void) SelectObject(hdc, oldFont);
\r
6978 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
6984 if( count <= 0 ) {
\r
6985 if (appData.debugMode) {
\r
6986 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
6989 return ERROR_INVALID_USER_BUFFER;
\r
6992 ResetEvent(ovl->hEvent);
\r
6993 ovl->Offset = ovl->OffsetHigh = 0;
\r
6994 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
6998 err = GetLastError();
\r
6999 if (err == ERROR_IO_PENDING) {
\r
7000 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7004 err = GetLastError();
\r
7011 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7016 ResetEvent(ovl->hEvent);
\r
7017 ovl->Offset = ovl->OffsetHigh = 0;
\r
7018 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7022 err = GetLastError();
\r
7023 if (err == ERROR_IO_PENDING) {
\r
7024 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7028 err = GetLastError();
\r
7034 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7035 void CheckForInputBufferFull( InputSource * is )
\r
7037 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7038 /* Look for end of line */
\r
7039 char * p = is->buf;
\r
7041 while( p < is->next && *p != '\n' ) {
\r
7045 if( p >= is->next ) {
\r
7046 if (appData.debugMode) {
\r
7047 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7050 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7051 is->count = (DWORD) -1;
\r
7052 is->next = is->buf;
\r
7058 InputThread(LPVOID arg)
\r
7063 is = (InputSource *) arg;
\r
7064 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7065 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7066 while (is->hThread != NULL) {
\r
7067 is->error = DoReadFile(is->hFile, is->next,
\r
7068 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7069 &is->count, &ovl);
\r
7070 if (is->error == NO_ERROR) {
\r
7071 is->next += is->count;
\r
7073 if (is->error == ERROR_BROKEN_PIPE) {
\r
7074 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7077 is->count = (DWORD) -1;
\r
7078 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7083 CheckForInputBufferFull( is );
\r
7085 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7087 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7089 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7092 CloseHandle(ovl.hEvent);
\r
7093 CloseHandle(is->hFile);
\r
7095 if (appData.debugMode) {
\r
7096 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7103 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7105 NonOvlInputThread(LPVOID arg)
\r
7112 is = (InputSource *) arg;
\r
7113 while (is->hThread != NULL) {
\r
7114 is->error = ReadFile(is->hFile, is->next,
\r
7115 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7116 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7117 if (is->error == NO_ERROR) {
\r
7118 /* Change CRLF to LF */
\r
7119 if (is->next > is->buf) {
\r
7121 i = is->count + 1;
\r
7129 if (prev == '\r' && *p == '\n') {
\r
7141 if (is->error == ERROR_BROKEN_PIPE) {
\r
7142 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7145 is->count = (DWORD) -1;
\r
7149 CheckForInputBufferFull( is );
\r
7151 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7153 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7155 if (is->count < 0) break; /* Quit on error */
\r
7157 CloseHandle(is->hFile);
\r
7162 SocketInputThread(LPVOID arg)
\r
7166 is = (InputSource *) arg;
\r
7167 while (is->hThread != NULL) {
\r
7168 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7169 if ((int)is->count == SOCKET_ERROR) {
\r
7170 is->count = (DWORD) -1;
\r
7171 is->error = WSAGetLastError();
\r
7173 is->error = NO_ERROR;
\r
7174 is->next += is->count;
\r
7175 if (is->count == 0 && is->second == is) {
\r
7176 /* End of file on stderr; quit with no message */
\r
7180 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7182 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7184 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7190 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7194 is = (InputSource *) lParam;
\r
7195 if (is->lineByLine) {
\r
7196 /* Feed in lines one by one */
\r
7197 char *p = is->buf;
\r
7199 while (q < is->next) {
\r
7200 if (*q++ == '\n') {
\r
7201 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7206 /* Move any partial line to the start of the buffer */
\r
7208 while (p < is->next) {
\r
7213 if (is->error != NO_ERROR || is->count == 0) {
\r
7214 /* Notify backend of the error. Note: If there was a partial
\r
7215 line at the end, it is not flushed through. */
\r
7216 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7219 /* Feed in the whole chunk of input at once */
\r
7220 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7221 is->next = is->buf;
\r
7225 /*---------------------------------------------------------------------------*\
\r
7227 * Menu enables. Used when setting various modes.
\r
7229 \*---------------------------------------------------------------------------*/
\r
7237 GreyRevert(Boolean grey)
\r
7238 { // [HGM] vari: for retracting variations in local mode
\r
7239 HMENU hmenu = GetMenu(hwndMain);
\r
7240 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7244 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7246 while (enab->item > 0) {
\r
7247 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7252 Enables gnuEnables[] = {
\r
7253 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7254 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7255 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7256 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7257 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7258 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7259 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7260 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7261 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7262 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7263 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7267 Enables icsEnables[] = {
\r
7268 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7269 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7270 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7271 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7272 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7273 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7274 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7275 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7276 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7277 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7278 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7279 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7280 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7281 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7282 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7287 Enables zippyEnables[] = {
\r
7288 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7289 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7290 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7291 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7296 Enables ncpEnables[] = {
\r
7297 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7298 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7299 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7300 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7301 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7302 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7303 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7304 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7305 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7306 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7307 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7308 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7309 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7310 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7311 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7312 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7313 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7314 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7315 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7319 Enables trainingOnEnables[] = {
\r
7320 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7321 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7322 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7323 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7324 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7325 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7326 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7327 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7331 Enables trainingOffEnables[] = {
\r
7332 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7333 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7334 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7335 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7336 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7337 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7338 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7339 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7343 /* These modify either ncpEnables or gnuEnables */
\r
7344 Enables cmailEnables[] = {
\r
7345 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7346 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7347 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7348 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7349 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7350 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7351 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7355 Enables machineThinkingEnables[] = {
\r
7356 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7357 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7358 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7359 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7360 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7361 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7362 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7363 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7364 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7365 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7366 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7367 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7368 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7369 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7370 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7374 Enables userThinkingEnables[] = {
\r
7375 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7376 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7377 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7378 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7379 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7380 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7381 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7382 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7383 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7384 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7385 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7386 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7387 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7388 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7389 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7393 /*---------------------------------------------------------------------------*\
\r
7395 * Front-end interface functions exported by XBoard.
\r
7396 * Functions appear in same order as prototypes in frontend.h.
\r
7398 \*---------------------------------------------------------------------------*/
\r
7402 static UINT prevChecked = 0;
\r
7403 static int prevPausing = 0;
\r
7406 if (pausing != prevPausing) {
\r
7407 prevPausing = pausing;
\r
7408 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7409 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7410 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7413 switch (gameMode) {
\r
7414 case BeginningOfGame:
\r
7415 if (appData.icsActive)
\r
7416 nowChecked = IDM_IcsClient;
\r
7417 else if (appData.noChessProgram)
\r
7418 nowChecked = IDM_EditGame;
\r
7420 nowChecked = IDM_MachineBlack;
\r
7422 case MachinePlaysBlack:
\r
7423 nowChecked = IDM_MachineBlack;
\r
7425 case MachinePlaysWhite:
\r
7426 nowChecked = IDM_MachineWhite;
\r
7428 case TwoMachinesPlay:
\r
7429 nowChecked = IDM_TwoMachines;
\r
7432 nowChecked = IDM_AnalysisMode;
\r
7435 nowChecked = IDM_AnalyzeFile;
\r
7438 nowChecked = IDM_EditGame;
\r
7440 case PlayFromGameFile:
\r
7441 nowChecked = IDM_LoadGame;
\r
7443 case EditPosition:
\r
7444 nowChecked = IDM_EditPosition;
\r
7447 nowChecked = IDM_Training;
\r
7449 case IcsPlayingWhite:
\r
7450 case IcsPlayingBlack:
\r
7451 case IcsObserving:
\r
7453 nowChecked = IDM_IcsClient;
\r
7460 if (prevChecked != 0)
\r
7461 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7462 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7463 if (nowChecked != 0)
\r
7464 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7465 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7467 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7468 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7469 MF_BYCOMMAND|MF_ENABLED);
\r
7471 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7472 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7475 prevChecked = nowChecked;
\r
7477 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7478 if (appData.icsActive) {
\r
7479 if (appData.icsEngineAnalyze) {
\r
7480 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7481 MF_BYCOMMAND|MF_CHECKED);
\r
7483 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7484 MF_BYCOMMAND|MF_UNCHECKED);
\r
7492 HMENU hmenu = GetMenu(hwndMain);
\r
7493 SetMenuEnables(hmenu, icsEnables);
\r
7494 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7495 MF_BYPOSITION|MF_ENABLED);
\r
7497 if (appData.zippyPlay) {
\r
7498 SetMenuEnables(hmenu, zippyEnables);
\r
7499 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7500 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7501 MF_BYCOMMAND|MF_ENABLED);
\r
7509 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7515 HMENU hmenu = GetMenu(hwndMain);
\r
7516 SetMenuEnables(hmenu, ncpEnables);
\r
7517 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7518 MF_BYPOSITION|MF_GRAYED);
\r
7519 DrawMenuBar(hwndMain);
\r
7525 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7529 SetTrainingModeOn()
\r
7532 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7533 for (i = 0; i < N_BUTTONS; i++) {
\r
7534 if (buttonDesc[i].hwnd != NULL)
\r
7535 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7540 VOID SetTrainingModeOff()
\r
7543 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7544 for (i = 0; i < N_BUTTONS; i++) {
\r
7545 if (buttonDesc[i].hwnd != NULL)
\r
7546 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7552 SetUserThinkingEnables()
\r
7554 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7558 SetMachineThinkingEnables()
\r
7560 HMENU hMenu = GetMenu(hwndMain);
\r
7561 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7563 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7565 if (gameMode == MachinePlaysBlack) {
\r
7566 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7567 } else if (gameMode == MachinePlaysWhite) {
\r
7568 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7569 } else if (gameMode == TwoMachinesPlay) {
\r
7570 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7576 DisplayTitle(char *str)
\r
7578 char title[MSG_SIZ], *host;
\r
7579 if (str[0] != NULLCHAR) {
\r
7580 strcpy(title, str);
\r
7581 } else if (appData.icsActive) {
\r
7582 if (appData.icsCommPort[0] != NULLCHAR)
\r
7585 host = appData.icsHost;
\r
7586 sprintf(title, "%s: %s", szTitle, host);
\r
7587 } else if (appData.noChessProgram) {
\r
7588 strcpy(title, szTitle);
\r
7590 strcpy(title, szTitle);
\r
7591 strcat(title, ": ");
\r
7592 strcat(title, first.tidy);
\r
7594 SetWindowText(hwndMain, title);
\r
7599 DisplayMessage(char *str1, char *str2)
\r
7603 int remain = MESSAGE_TEXT_MAX - 1;
\r
7606 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7607 messageText[0] = NULLCHAR;
\r
7609 len = strlen(str1);
\r
7610 if (len > remain) len = remain;
\r
7611 strncpy(messageText, str1, len);
\r
7612 messageText[len] = NULLCHAR;
\r
7615 if (*str2 && remain >= 2) {
\r
7617 strcat(messageText, " ");
\r
7620 len = strlen(str2);
\r
7621 if (len > remain) len = remain;
\r
7622 strncat(messageText, str2, len);
\r
7624 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7626 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7630 hdc = GetDC(hwndMain);
\r
7631 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7632 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7633 &messageRect, messageText, strlen(messageText), NULL);
\r
7634 (void) SelectObject(hdc, oldFont);
\r
7635 (void) ReleaseDC(hwndMain, hdc);
\r
7639 DisplayError(char *str, int error)
\r
7641 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7647 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7648 NULL, error, LANG_NEUTRAL,
\r
7649 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7651 sprintf(buf, "%s:\n%s", str, buf2);
\r
7653 ErrorMap *em = errmap;
\r
7654 while (em->err != 0 && em->err != error) em++;
\r
7655 if (em->err != 0) {
\r
7656 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7658 sprintf(buf, "%s:\nError code %d", str, error);
\r
7663 ErrorPopUp("Error", buf);
\r
7668 DisplayMoveError(char *str)
\r
7670 fromX = fromY = -1;
\r
7671 ClearHighlights();
\r
7672 DrawPosition(FALSE, NULL);
\r
7673 if (appData.popupMoveErrors) {
\r
7674 ErrorPopUp("Error", str);
\r
7676 DisplayMessage(str, "");
\r
7677 moveErrorMessageUp = TRUE;
\r
7682 DisplayFatalError(char *str, int error, int exitStatus)
\r
7684 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7686 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7689 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7690 NULL, error, LANG_NEUTRAL,
\r
7691 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7693 sprintf(buf, "%s:\n%s", str, buf2);
\r
7695 ErrorMap *em = errmap;
\r
7696 while (em->err != 0 && em->err != error) em++;
\r
7697 if (em->err != 0) {
\r
7698 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7700 sprintf(buf, "%s:\nError code %d", str, error);
\r
7705 if (appData.debugMode) {
\r
7706 fprintf(debugFP, "%s: %s\n", label, str);
\r
7708 if (appData.popupExitMessage) {
\r
7709 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7710 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7712 ExitEvent(exitStatus);
\r
7717 DisplayInformation(char *str)
\r
7719 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7724 DisplayNote(char *str)
\r
7726 ErrorPopUp("Note", str);
\r
7731 char *title, *question, *replyPrefix;
\r
7736 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7738 static QuestionParams *qp;
\r
7739 char reply[MSG_SIZ];
\r
7742 switch (message) {
\r
7743 case WM_INITDIALOG:
\r
7744 qp = (QuestionParams *) lParam;
\r
7745 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7746 SetWindowText(hDlg, qp->title);
\r
7747 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7748 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7752 switch (LOWORD(wParam)) {
\r
7754 strcpy(reply, qp->replyPrefix);
\r
7755 if (*reply) strcat(reply, " ");
\r
7756 len = strlen(reply);
\r
7757 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7758 strcat(reply, "\n");
\r
7759 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7760 EndDialog(hDlg, TRUE);
\r
7761 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7764 EndDialog(hDlg, FALSE);
\r
7775 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7777 QuestionParams qp;
\r
7781 qp.question = question;
\r
7782 qp.replyPrefix = replyPrefix;
\r
7784 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7785 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7786 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7787 FreeProcInstance(lpProc);
\r
7790 /* [AS] Pick FRC position */
\r
7791 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7793 static int * lpIndexFRC;
\r
7799 case WM_INITDIALOG:
\r
7800 lpIndexFRC = (int *) lParam;
\r
7802 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7804 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7805 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7806 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7807 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7812 switch( LOWORD(wParam) ) {
\r
7814 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7815 EndDialog( hDlg, 0 );
\r
7816 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7819 EndDialog( hDlg, 1 );
\r
7821 case IDC_NFG_Edit:
\r
7822 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7823 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7825 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7828 case IDC_NFG_Random:
\r
7829 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7830 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7843 int index = appData.defaultFrcPosition;
\r
7844 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7846 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7848 if( result == 0 ) {
\r
7849 appData.defaultFrcPosition = index;
\r
7855 /* [AS] Game list options */
\r
7861 static GLT_Item GLT_ItemInfo[] = {
\r
7862 { GLT_EVENT, "Event" },
\r
7863 { GLT_SITE, "Site" },
\r
7864 { GLT_DATE, "Date" },
\r
7865 { GLT_ROUND, "Round" },
\r
7866 { GLT_PLAYERS, "Players" },
\r
7867 { GLT_RESULT, "Result" },
\r
7868 { GLT_WHITE_ELO, "White Rating" },
\r
7869 { GLT_BLACK_ELO, "Black Rating" },
\r
7870 { GLT_TIME_CONTROL,"Time Control" },
\r
7871 { GLT_VARIANT, "Variant" },
\r
7872 { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },
\r
7873 { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom
\r
7877 const char * GLT_FindItem( char id )
\r
7879 const char * result = 0;
\r
7881 GLT_Item * list = GLT_ItemInfo;
\r
7883 while( list->id != 0 ) {
\r
7884 if( list->id == id ) {
\r
7885 result = list->name;
\r
7895 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )
\r
7897 const char * name = GLT_FindItem( id );
\r
7900 if( index >= 0 ) {
\r
7901 SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );
\r
7904 SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );
\r
7909 void GLT_TagsToList( HWND hDlg, char * tags )
\r
7913 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7916 GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
\r
7920 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );
\r
7922 pc = GLT_ALL_TAGS;
\r
7925 if( strchr( tags, *pc ) == 0 ) {
\r
7926 GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
\r
7931 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7934 char GLT_ListItemToTag( HWND hDlg, int index )
\r
7936 char result = '\0';
\r
7939 GLT_Item * list = GLT_ItemInfo;
\r
7941 if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {
\r
7942 while( list->id != 0 ) {
\r
7943 if( strcmp( list->name, name ) == 0 ) {
\r
7944 result = list->id;
\r
7955 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7957 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7958 int idx2 = idx1 + delta;
\r
7959 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7961 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7964 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7965 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7966 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7967 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7971 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7973 static char glt[64];
\r
7974 static char * lpUserGLT;
\r
7978 case WM_INITDIALOG:
\r
7979 lpUserGLT = (char *) lParam;
\r
7981 strcpy( glt, lpUserGLT );
\r
7983 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7985 /* Initialize list */
\r
7986 GLT_TagsToList( hDlg, glt );
\r
7988 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
7993 switch( LOWORD(wParam) ) {
\r
7996 char * pc = lpUserGLT;
\r
7998 // int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8002 id = GLT_ListItemToTag( hDlg, idx );
\r
8006 } while( id != '\0' );
\r
8008 EndDialog( hDlg, 0 );
\r
8011 EndDialog( hDlg, 1 );
\r
8014 case IDC_GLT_Default:
\r
8015 strcpy( glt, GLT_DEFAULT_TAGS );
\r
8016 GLT_TagsToList( hDlg, glt );
\r
8019 case IDC_GLT_Restore:
\r
8020 strcpy( glt, lpUserGLT );
\r
8021 GLT_TagsToList( hDlg, glt );
\r
8025 GLT_MoveSelection( hDlg, -1 );
\r
8028 case IDC_GLT_Down:
\r
8029 GLT_MoveSelection( hDlg, +1 );
\r
8039 int GameListOptions()
\r
8043 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8045 strcpy( glt, appData.gameListTags );
\r
8047 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );
\r
8049 if( result == 0 ) {
\r
8050 /* [AS] Memory leak here! */
\r
8051 appData.gameListTags = strdup( glt );
\r
8059 DisplayIcsInteractionTitle(char *str)
\r
8061 char consoleTitle[MSG_SIZ];
\r
8063 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8064 SetWindowText(hwndConsole, consoleTitle);
\r
8068 DrawPosition(int fullRedraw, Board board)
\r
8070 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8073 void NotifyFrontendLogin()
\r
8076 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8082 fromX = fromY = -1;
\r
8083 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8084 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8085 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8086 dragInfo.lastpos = dragInfo.pos;
\r
8087 dragInfo.start.x = dragInfo.start.y = -1;
\r
8088 dragInfo.from = dragInfo.start;
\r
8090 DrawPosition(TRUE, NULL);
\r
8096 CommentPopUp(char *title, char *str)
\r
8098 HWND hwnd = GetActiveWindow();
\r
8099 EitherCommentPopUp(0, title, str, FALSE);
\r
8101 SetActiveWindow(hwnd);
\r
8105 CommentPopDown(void)
\r
8107 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8108 if (commentDialog) {
\r
8109 ShowWindow(commentDialog, SW_HIDE);
\r
8111 commentUp = FALSE;
\r
8115 EditCommentPopUp(int index, char *title, char *str)
\r
8117 EitherCommentPopUp(index, title, str, TRUE);
\r
8124 MyPlaySound(&sounds[(int)SoundMove]);
\r
8127 VOID PlayIcsWinSound()
\r
8129 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8132 VOID PlayIcsLossSound()
\r
8134 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8137 VOID PlayIcsDrawSound()
\r
8139 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8142 VOID PlayIcsUnfinishedSound()
\r
8144 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8150 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8158 consoleEcho = TRUE;
\r
8159 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8160 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8161 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8170 consoleEcho = FALSE;
\r
8171 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8172 /* This works OK: set text and background both to the same color */
\r
8174 cf.crTextColor = COLOR_ECHOOFF;
\r
8175 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8176 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8179 /* No Raw()...? */
\r
8181 void Colorize(ColorClass cc, int continuation)
\r
8183 currentColorClass = cc;
\r
8184 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8185 consoleCF.crTextColor = textAttribs[cc].color;
\r
8186 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8187 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8193 static char buf[MSG_SIZ];
\r
8194 DWORD bufsiz = MSG_SIZ;
\r
8196 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8197 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8199 if (!GetUserName(buf, &bufsiz)) {
\r
8200 /*DisplayError("Error getting user name", GetLastError());*/
\r
8201 strcpy(buf, "User");
\r
8209 static char buf[MSG_SIZ];
\r
8210 DWORD bufsiz = MSG_SIZ;
\r
8212 if (!GetComputerName(buf, &bufsiz)) {
\r
8213 /*DisplayError("Error getting host name", GetLastError());*/
\r
8214 strcpy(buf, "Unknown");
\r
8221 ClockTimerRunning()
\r
8223 return clockTimerEvent != 0;
\r
8229 if (clockTimerEvent == 0) return FALSE;
\r
8230 KillTimer(hwndMain, clockTimerEvent);
\r
8231 clockTimerEvent = 0;
\r
8236 StartClockTimer(long millisec)
\r
8238 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8239 (UINT) millisec, NULL);
\r
8243 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8246 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8248 if(appData.noGUI) return;
\r
8249 hdc = GetDC(hwndMain);
\r
8250 if (!IsIconic(hwndMain)) {
\r
8251 DisplayAClock(hdc, timeRemaining, highlight,
\r
8252 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8254 if (highlight && iconCurrent == iconBlack) {
\r
8255 iconCurrent = iconWhite;
\r
8256 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8257 if (IsIconic(hwndMain)) {
\r
8258 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8261 (void) ReleaseDC(hwndMain, hdc);
\r
8263 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8267 DisplayBlackClock(long timeRemaining, int highlight)
\r
8270 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8272 if(appData.noGUI) return;
\r
8273 hdc = GetDC(hwndMain);
\r
8274 if (!IsIconic(hwndMain)) {
\r
8275 DisplayAClock(hdc, timeRemaining, highlight,
\r
8276 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8278 if (highlight && iconCurrent == iconWhite) {
\r
8279 iconCurrent = iconBlack;
\r
8280 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8281 if (IsIconic(hwndMain)) {
\r
8282 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8285 (void) ReleaseDC(hwndMain, hdc);
\r
8287 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8292 LoadGameTimerRunning()
\r
8294 return loadGameTimerEvent != 0;
\r
8298 StopLoadGameTimer()
\r
8300 if (loadGameTimerEvent == 0) return FALSE;
\r
8301 KillTimer(hwndMain, loadGameTimerEvent);
\r
8302 loadGameTimerEvent = 0;
\r
8307 StartLoadGameTimer(long millisec)
\r
8309 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8310 (UINT) millisec, NULL);
\r
8318 char fileTitle[MSG_SIZ];
\r
8320 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8321 f = OpenFileDialog(hwndMain, "a", defName,
\r
8322 appData.oldSaveStyle ? "gam" : "pgn",
\r
8324 "Save Game to File", NULL, fileTitle, NULL);
\r
8326 SaveGame(f, 0, "");
\r
8333 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8335 if (delayedTimerEvent != 0) {
\r
8336 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8337 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8339 KillTimer(hwndMain, delayedTimerEvent);
\r
8340 delayedTimerEvent = 0;
\r
8341 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8342 delayedTimerCallback();
\r
8344 delayedTimerCallback = cb;
\r
8345 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8346 (UINT) millisec, NULL);
\r
8349 DelayedEventCallback
\r
8352 if (delayedTimerEvent) {
\r
8353 return delayedTimerCallback;
\r
8360 CancelDelayedEvent()
\r
8362 if (delayedTimerEvent) {
\r
8363 KillTimer(hwndMain, delayedTimerEvent);
\r
8364 delayedTimerEvent = 0;
\r
8368 DWORD GetWin32Priority(int nice)
\r
8369 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8371 REALTIME_PRIORITY_CLASS 0x00000100
\r
8372 HIGH_PRIORITY_CLASS 0x00000080
\r
8373 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8374 NORMAL_PRIORITY_CLASS 0x00000020
\r
8375 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8376 IDLE_PRIORITY_CLASS 0x00000040
\r
8378 if (nice < -15) return 0x00000080;
\r
8379 if (nice < 0) return 0x00008000;
\r
8380 if (nice == 0) return 0x00000020;
\r
8381 if (nice < 15) return 0x00004000;
\r
8382 return 0x00000040;
\r
8385 /* Start a child process running the given program.
\r
8386 The process's standard output can be read from "from", and its
\r
8387 standard input can be written to "to".
\r
8388 Exit with fatal error if anything goes wrong.
\r
8389 Returns an opaque pointer that can be used to destroy the process
\r
8393 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8395 #define BUFSIZE 4096
\r
8397 HANDLE hChildStdinRd, hChildStdinWr,
\r
8398 hChildStdoutRd, hChildStdoutWr;
\r
8399 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8400 SECURITY_ATTRIBUTES saAttr;
\r
8402 PROCESS_INFORMATION piProcInfo;
\r
8403 STARTUPINFO siStartInfo;
\r
8405 char buf[MSG_SIZ];
\r
8408 if (appData.debugMode) {
\r
8409 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8414 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8415 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8416 saAttr.bInheritHandle = TRUE;
\r
8417 saAttr.lpSecurityDescriptor = NULL;
\r
8420 * The steps for redirecting child's STDOUT:
\r
8421 * 1. Create anonymous pipe to be STDOUT for child.
\r
8422 * 2. Create a noninheritable duplicate of read handle,
\r
8423 * and close the inheritable read handle.
\r
8426 /* Create a pipe for the child's STDOUT. */
\r
8427 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8428 return GetLastError();
\r
8431 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8432 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8433 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8434 FALSE, /* not inherited */
\r
8435 DUPLICATE_SAME_ACCESS);
\r
8437 return GetLastError();
\r
8439 CloseHandle(hChildStdoutRd);
\r
8442 * The steps for redirecting child's STDIN:
\r
8443 * 1. Create anonymous pipe to be STDIN for child.
\r
8444 * 2. Create a noninheritable duplicate of write handle,
\r
8445 * and close the inheritable write handle.
\r
8448 /* Create a pipe for the child's STDIN. */
\r
8449 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8450 return GetLastError();
\r
8453 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8454 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8455 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8456 FALSE, /* not inherited */
\r
8457 DUPLICATE_SAME_ACCESS);
\r
8459 return GetLastError();
\r
8461 CloseHandle(hChildStdinWr);
\r
8463 /* Arrange to (1) look in dir for the child .exe file, and
\r
8464 * (2) have dir be the child's working directory. Interpret
\r
8465 * dir relative to the directory WinBoard loaded from. */
\r
8466 GetCurrentDirectory(MSG_SIZ, buf);
\r
8467 SetCurrentDirectory(installDir);
\r
8468 SetCurrentDirectory(dir);
\r
8470 /* Now create the child process. */
\r
8472 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8473 siStartInfo.lpReserved = NULL;
\r
8474 siStartInfo.lpDesktop = NULL;
\r
8475 siStartInfo.lpTitle = NULL;
\r
8476 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8477 siStartInfo.cbReserved2 = 0;
\r
8478 siStartInfo.lpReserved2 = NULL;
\r
8479 siStartInfo.hStdInput = hChildStdinRd;
\r
8480 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8481 siStartInfo.hStdError = hChildStdoutWr;
\r
8483 fSuccess = CreateProcess(NULL,
\r
8484 cmdLine, /* command line */
\r
8485 NULL, /* process security attributes */
\r
8486 NULL, /* primary thread security attrs */
\r
8487 TRUE, /* handles are inherited */
\r
8488 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8489 NULL, /* use parent's environment */
\r
8491 &siStartInfo, /* STARTUPINFO pointer */
\r
8492 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8494 err = GetLastError();
\r
8495 SetCurrentDirectory(buf); /* return to prev directory */
\r
8500 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8501 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8502 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8505 /* Close the handles we don't need in the parent */
\r
8506 CloseHandle(piProcInfo.hThread);
\r
8507 CloseHandle(hChildStdinRd);
\r
8508 CloseHandle(hChildStdoutWr);
\r
8510 /* Prepare return value */
\r
8511 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8512 cp->kind = CPReal;
\r
8513 cp->hProcess = piProcInfo.hProcess;
\r
8514 cp->pid = piProcInfo.dwProcessId;
\r
8515 cp->hFrom = hChildStdoutRdDup;
\r
8516 cp->hTo = hChildStdinWrDup;
\r
8518 *pr = (void *) cp;
\r
8520 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8521 2000 where engines sometimes don't see the initial command(s)
\r
8522 from WinBoard and hang. I don't understand how that can happen,
\r
8523 but the Sleep is harmless, so I've put it in. Others have also
\r
8524 reported what may be the same problem, so hopefully this will fix
\r
8525 it for them too. */
\r
8533 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8535 ChildProc *cp; int result;
\r
8537 cp = (ChildProc *) pr;
\r
8538 if (cp == NULL) return;
\r
8540 switch (cp->kind) {
\r
8542 /* TerminateProcess is considered harmful, so... */
\r
8543 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8544 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8545 /* The following doesn't work because the chess program
\r
8546 doesn't "have the same console" as WinBoard. Maybe
\r
8547 we could arrange for this even though neither WinBoard
\r
8548 nor the chess program uses a console for stdio? */
\r
8549 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8551 /* [AS] Special termination modes for misbehaving programs... */
\r
8552 if( signal == 9 ) {
\r
8553 result = TerminateProcess( cp->hProcess, 0 );
\r
8555 if ( appData.debugMode) {
\r
8556 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8559 else if( signal == 10 ) {
\r
8560 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8562 if( dw != WAIT_OBJECT_0 ) {
\r
8563 result = TerminateProcess( cp->hProcess, 0 );
\r
8565 if ( appData.debugMode) {
\r
8566 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8572 CloseHandle(cp->hProcess);
\r
8576 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8580 closesocket(cp->sock);
\r
8585 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8586 closesocket(cp->sock);
\r
8587 closesocket(cp->sock2);
\r
8595 InterruptChildProcess(ProcRef pr)
\r
8599 cp = (ChildProc *) pr;
\r
8600 if (cp == NULL) return;
\r
8601 switch (cp->kind) {
\r
8603 /* The following doesn't work because the chess program
\r
8604 doesn't "have the same console" as WinBoard. Maybe
\r
8605 we could arrange for this even though neither WinBoard
\r
8606 nor the chess program uses a console for stdio */
\r
8607 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8612 /* Can't interrupt */
\r
8616 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8623 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8625 char cmdLine[MSG_SIZ];
\r
8627 if (port[0] == NULLCHAR) {
\r
8628 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8630 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8632 return StartChildProcess(cmdLine, "", pr);
\r
8636 /* Code to open TCP sockets */
\r
8639 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8644 struct sockaddr_in sa, mysa;
\r
8645 struct hostent FAR *hp;
\r
8646 unsigned short uport;
\r
8647 WORD wVersionRequested;
\r
8650 /* Initialize socket DLL */
\r
8651 wVersionRequested = MAKEWORD(1, 1);
\r
8652 err = WSAStartup(wVersionRequested, &wsaData);
\r
8653 if (err != 0) return err;
\r
8656 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8657 err = WSAGetLastError();
\r
8662 /* Bind local address using (mostly) don't-care values.
\r
8664 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8665 mysa.sin_family = AF_INET;
\r
8666 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8667 uport = (unsigned short) 0;
\r
8668 mysa.sin_port = htons(uport);
\r
8669 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8670 == SOCKET_ERROR) {
\r
8671 err = WSAGetLastError();
\r
8676 /* Resolve remote host name */
\r
8677 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8678 if (!(hp = gethostbyname(host))) {
\r
8679 unsigned int b0, b1, b2, b3;
\r
8681 err = WSAGetLastError();
\r
8683 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8684 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8685 hp->h_addrtype = AF_INET;
\r
8687 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8688 hp->h_addr_list[0] = (char *) malloc(4);
\r
8689 hp->h_addr_list[0][0] = (char) b0;
\r
8690 hp->h_addr_list[0][1] = (char) b1;
\r
8691 hp->h_addr_list[0][2] = (char) b2;
\r
8692 hp->h_addr_list[0][3] = (char) b3;
\r
8698 sa.sin_family = hp->h_addrtype;
\r
8699 uport = (unsigned short) atoi(port);
\r
8700 sa.sin_port = htons(uport);
\r
8701 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8703 /* Make connection */
\r
8704 if (connect(s, (struct sockaddr *) &sa,
\r
8705 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8706 err = WSAGetLastError();
\r
8711 /* Prepare return value */
\r
8712 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8713 cp->kind = CPSock;
\r
8715 *pr = (ProcRef *) cp;
\r
8721 OpenCommPort(char *name, ProcRef *pr)
\r
8726 char fullname[MSG_SIZ];
\r
8728 if (*name != '\\')
\r
8729 sprintf(fullname, "\\\\.\\%s", name);
\r
8731 strcpy(fullname, name);
\r
8733 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8734 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8735 if (h == (HANDLE) -1) {
\r
8736 return GetLastError();
\r
8740 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8742 /* Accumulate characters until a 100ms pause, then parse */
\r
8743 ct.ReadIntervalTimeout = 100;
\r
8744 ct.ReadTotalTimeoutMultiplier = 0;
\r
8745 ct.ReadTotalTimeoutConstant = 0;
\r
8746 ct.WriteTotalTimeoutMultiplier = 0;
\r
8747 ct.WriteTotalTimeoutConstant = 0;
\r
8748 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8750 /* Prepare return value */
\r
8751 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8752 cp->kind = CPComm;
\r
8755 *pr = (ProcRef *) cp;
\r
8761 OpenLoopback(ProcRef *pr)
\r
8763 DisplayFatalError("Not implemented", 0, 1);
\r
8769 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8774 struct sockaddr_in sa, mysa;
\r
8775 struct hostent FAR *hp;
\r
8776 unsigned short uport;
\r
8777 WORD wVersionRequested;
\r
8780 char stderrPortStr[MSG_SIZ];
\r
8782 /* Initialize socket DLL */
\r
8783 wVersionRequested = MAKEWORD(1, 1);
\r
8784 err = WSAStartup(wVersionRequested, &wsaData);
\r
8785 if (err != 0) return err;
\r
8787 /* Resolve remote host name */
\r
8788 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8789 if (!(hp = gethostbyname(host))) {
\r
8790 unsigned int b0, b1, b2, b3;
\r
8792 err = WSAGetLastError();
\r
8794 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8795 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8796 hp->h_addrtype = AF_INET;
\r
8798 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8799 hp->h_addr_list[0] = (char *) malloc(4);
\r
8800 hp->h_addr_list[0][0] = (char) b0;
\r
8801 hp->h_addr_list[0][1] = (char) b1;
\r
8802 hp->h_addr_list[0][2] = (char) b2;
\r
8803 hp->h_addr_list[0][3] = (char) b3;
\r
8809 sa.sin_family = hp->h_addrtype;
\r
8810 uport = (unsigned short) 514;
\r
8811 sa.sin_port = htons(uport);
\r
8812 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8814 /* Bind local socket to unused "privileged" port address
\r
8816 s = INVALID_SOCKET;
\r
8817 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8818 mysa.sin_family = AF_INET;
\r
8819 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8820 for (fromPort = 1023;; fromPort--) {
\r
8821 if (fromPort < 0) {
\r
8823 return WSAEADDRINUSE;
\r
8825 if (s == INVALID_SOCKET) {
\r
8826 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8827 err = WSAGetLastError();
\r
8832 uport = (unsigned short) fromPort;
\r
8833 mysa.sin_port = htons(uport);
\r
8834 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8835 == SOCKET_ERROR) {
\r
8836 err = WSAGetLastError();
\r
8837 if (err == WSAEADDRINUSE) continue;
\r
8841 if (connect(s, (struct sockaddr *) &sa,
\r
8842 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8843 err = WSAGetLastError();
\r
8844 if (err == WSAEADDRINUSE) {
\r
8855 /* Bind stderr local socket to unused "privileged" port address
\r
8857 s2 = INVALID_SOCKET;
\r
8858 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8859 mysa.sin_family = AF_INET;
\r
8860 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8861 for (fromPort = 1023;; fromPort--) {
\r
8862 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8863 if (fromPort < 0) {
\r
8864 (void) closesocket(s);
\r
8866 return WSAEADDRINUSE;
\r
8868 if (s2 == INVALID_SOCKET) {
\r
8869 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8870 err = WSAGetLastError();
\r
8876 uport = (unsigned short) fromPort;
\r
8877 mysa.sin_port = htons(uport);
\r
8878 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8879 == SOCKET_ERROR) {
\r
8880 err = WSAGetLastError();
\r
8881 if (err == WSAEADDRINUSE) continue;
\r
8882 (void) closesocket(s);
\r
8886 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8887 err = WSAGetLastError();
\r
8888 if (err == WSAEADDRINUSE) {
\r
8890 s2 = INVALID_SOCKET;
\r
8893 (void) closesocket(s);
\r
8894 (void) closesocket(s2);
\r
8900 prevStderrPort = fromPort; // remember port used
\r
8901 sprintf(stderrPortStr, "%d", fromPort);
\r
8903 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8904 err = WSAGetLastError();
\r
8905 (void) closesocket(s);
\r
8906 (void) closesocket(s2);
\r
8911 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8912 err = WSAGetLastError();
\r
8913 (void) closesocket(s);
\r
8914 (void) closesocket(s2);
\r
8918 if (*user == NULLCHAR) user = UserName();
\r
8919 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8920 err = WSAGetLastError();
\r
8921 (void) closesocket(s);
\r
8922 (void) closesocket(s2);
\r
8926 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8927 err = WSAGetLastError();
\r
8928 (void) closesocket(s);
\r
8929 (void) closesocket(s2);
\r
8934 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8935 err = WSAGetLastError();
\r
8936 (void) closesocket(s);
\r
8937 (void) closesocket(s2);
\r
8941 (void) closesocket(s2); /* Stop listening */
\r
8943 /* Prepare return value */
\r
8944 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8945 cp->kind = CPRcmd;
\r
8948 *pr = (ProcRef *) cp;
\r
8955 AddInputSource(ProcRef pr, int lineByLine,
\r
8956 InputCallback func, VOIDSTAR closure)
\r
8958 InputSource *is, *is2 = NULL;
\r
8959 ChildProc *cp = (ChildProc *) pr;
\r
8961 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8962 is->lineByLine = lineByLine;
\r
8964 is->closure = closure;
\r
8965 is->second = NULL;
\r
8966 is->next = is->buf;
\r
8967 if (pr == NoProc) {
\r
8968 is->kind = CPReal;
\r
8969 consoleInputSource = is;
\r
8971 is->kind = cp->kind;
\r
8973 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8974 we create all threads suspended so that the is->hThread variable can be
\r
8975 safely assigned, then let the threads start with ResumeThread.
\r
8977 switch (cp->kind) {
\r
8979 is->hFile = cp->hFrom;
\r
8980 cp->hFrom = NULL; /* now owned by InputThread */
\r
8982 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8983 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8987 is->hFile = cp->hFrom;
\r
8988 cp->hFrom = NULL; /* now owned by InputThread */
\r
8990 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
8991 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8995 is->sock = cp->sock;
\r
8997 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8998 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9002 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9004 is->sock = cp->sock;
\r
9006 is2->sock = cp->sock2;
\r
9007 is2->second = is2;
\r
9009 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9010 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9012 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9013 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9017 if( is->hThread != NULL ) {
\r
9018 ResumeThread( is->hThread );
\r
9021 if( is2 != NULL && is2->hThread != NULL ) {
\r
9022 ResumeThread( is2->hThread );
\r
9026 return (InputSourceRef) is;
\r
9030 RemoveInputSource(InputSourceRef isr)
\r
9034 is = (InputSource *) isr;
\r
9035 is->hThread = NULL; /* tell thread to stop */
\r
9036 CloseHandle(is->hThread);
\r
9037 if (is->second != NULL) {
\r
9038 is->second->hThread = NULL;
\r
9039 CloseHandle(is->second->hThread);
\r
9043 int no_wrap(char *message, int count)
\r
9045 ConsoleOutput(message, count, FALSE);
\r
9050 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9053 int outCount = SOCKET_ERROR;
\r
9054 ChildProc *cp = (ChildProc *) pr;
\r
9055 static OVERLAPPED ovl;
\r
9056 static int line = 0;
\r
9060 if (appData.noJoin || !appData.useInternalWrap)
\r
9061 return no_wrap(message, count);
\r
9064 int width = get_term_width();
\r
9065 int len = wrap(NULL, message, count, width, &line);
\r
9066 char *msg = malloc(len);
\r
9070 return no_wrap(message, count);
\r
9073 dbgchk = wrap(msg, message, count, width, &line);
\r
9074 if (dbgchk != len && appData.debugMode)
\r
9075 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9076 ConsoleOutput(msg, len, FALSE);
\r
9083 if (ovl.hEvent == NULL) {
\r
9084 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9086 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9088 switch (cp->kind) {
\r
9091 outCount = send(cp->sock, message, count, 0);
\r
9092 if (outCount == SOCKET_ERROR) {
\r
9093 *outError = WSAGetLastError();
\r
9095 *outError = NO_ERROR;
\r
9100 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9101 &dOutCount, NULL)) {
\r
9102 *outError = NO_ERROR;
\r
9103 outCount = (int) dOutCount;
\r
9105 *outError = GetLastError();
\r
9110 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9111 &dOutCount, &ovl);
\r
9112 if (*outError == NO_ERROR) {
\r
9113 outCount = (int) dOutCount;
\r
9121 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9124 /* Ignore delay, not implemented for WinBoard */
\r
9125 return OutputToProcess(pr, message, count, outError);
\r
9130 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9131 char *buf, int count, int error)
\r
9133 DisplayFatalError("Not implemented", 0, 1);
\r
9136 /* see wgamelist.c for Game List functions */
\r
9137 /* see wedittags.c for Edit Tags functions */
\r
9144 char buf[MSG_SIZ];
\r
9147 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9148 f = fopen(buf, "r");
\r
9150 ProcessICSInitScript(f);
\r
9158 StartAnalysisClock()
\r
9160 if (analysisTimerEvent) return;
\r
9161 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9162 (UINT) 2000, NULL);
\r
9166 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9168 highlightInfo.sq[0].x = fromX;
\r
9169 highlightInfo.sq[0].y = fromY;
\r
9170 highlightInfo.sq[1].x = toX;
\r
9171 highlightInfo.sq[1].y = toY;
\r
9177 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9178 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9182 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9184 premoveHighlightInfo.sq[0].x = fromX;
\r
9185 premoveHighlightInfo.sq[0].y = fromY;
\r
9186 premoveHighlightInfo.sq[1].x = toX;
\r
9187 premoveHighlightInfo.sq[1].y = toY;
\r
9191 ClearPremoveHighlights()
\r
9193 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9194 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9198 ShutDownFrontEnd()
\r
9200 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9201 DeleteClipboardTempFiles();
\r
9207 if (IsIconic(hwndMain))
\r
9208 ShowWindow(hwndMain, SW_RESTORE);
\r
9210 SetActiveWindow(hwndMain);
\r
9214 * Prototypes for animation support routines
\r
9216 static void ScreenSquare(int column, int row, POINT * pt);
\r
9217 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9218 POINT frames[], int * nFrames);
\r
9222 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9223 { // [HGM] atomic: animate blast wave
\r
9225 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9226 explodeInfo.fromX = fromX;
\r
9227 explodeInfo.fromY = fromY;
\r
9228 explodeInfo.toX = toX;
\r
9229 explodeInfo.toY = toY;
\r
9230 for(i=1; i<nFrames; i++) {
\r
9231 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9232 DrawPosition(FALSE, NULL);
\r
9233 Sleep(appData.animSpeed);
\r
9235 explodeInfo.radius = 0;
\r
9236 DrawPosition(TRUE, NULL);
\r
9242 AnimateMove(board, fromX, fromY, toX, toY)
\r
9249 ChessSquare piece;
\r
9250 POINT start, finish, mid;
\r
9251 POINT frames[kFactor * 2 + 1];
\r
9254 if (!appData.animate) return;
\r
9255 if (doingSizing) return;
\r
9256 if (fromY < 0 || fromX < 0) return;
\r
9257 piece = board[fromY][fromX];
\r
9258 if (piece >= EmptySquare) return;
\r
9260 ScreenSquare(fromX, fromY, &start);
\r
9261 ScreenSquare(toX, toY, &finish);
\r
9263 /* All pieces except knights move in straight line */
\r
9264 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9265 mid.x = start.x + (finish.x - start.x) / 2;
\r
9266 mid.y = start.y + (finish.y - start.y) / 2;
\r
9268 /* Knight: make diagonal movement then straight */
\r
9269 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9270 mid.x = start.x + (finish.x - start.x) / 2;
\r
9274 mid.y = start.y + (finish.y - start.y) / 2;
\r
9278 /* Don't use as many frames for very short moves */
\r
9279 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9280 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9282 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9284 animInfo.from.x = fromX;
\r
9285 animInfo.from.y = fromY;
\r
9286 animInfo.to.x = toX;
\r
9287 animInfo.to.y = toY;
\r
9288 animInfo.lastpos = start;
\r
9289 animInfo.piece = piece;
\r
9290 for (n = 0; n < nFrames; n++) {
\r
9291 animInfo.pos = frames[n];
\r
9292 DrawPosition(FALSE, NULL);
\r
9293 animInfo.lastpos = animInfo.pos;
\r
9294 Sleep(appData.animSpeed);
\r
9296 animInfo.pos = finish;
\r
9297 DrawPosition(FALSE, NULL);
\r
9298 animInfo.piece = EmptySquare;
\r
9299 if(gameInfo.variant == VariantAtomic &&
\r
9300 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9301 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9304 /* Convert board position to corner of screen rect and color */
\r
9307 ScreenSquare(column, row, pt)
\r
9308 int column; int row; POINT * pt;
\r
9311 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9312 pt->y = lineGap + row * (squareSize + lineGap);
\r
9314 pt->x = lineGap + column * (squareSize + lineGap);
\r
9315 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9319 /* Generate a series of frame coords from start->mid->finish.
\r
9320 The movement rate doubles until the half way point is
\r
9321 reached, then halves back down to the final destination,
\r
9322 which gives a nice slow in/out effect. The algorithmn
\r
9323 may seem to generate too many intermediates for short
\r
9324 moves, but remember that the purpose is to attract the
\r
9325 viewers attention to the piece about to be moved and
\r
9326 then to where it ends up. Too few frames would be less
\r
9330 Tween(start, mid, finish, factor, frames, nFrames)
\r
9331 POINT * start; POINT * mid;
\r
9332 POINT * finish; int factor;
\r
9333 POINT frames[]; int * nFrames;
\r
9335 int n, fraction = 1, count = 0;
\r
9337 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9338 for (n = 0; n < factor; n++)
\r
9340 for (n = 0; n < factor; n++) {
\r
9341 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9342 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9344 fraction = fraction / 2;
\r
9348 frames[count] = *mid;
\r
9351 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9353 for (n = 0; n < factor; n++) {
\r
9354 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9355 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9357 fraction = fraction * 2;
\r
9363 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9365 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9367 EvalGraphSet( first, last, current, pvInfoList );
\r