2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P(());
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 typedef struct { // [HGM] atomic
\r
139 int fromX, fromY, toX, toY, radius;
\r
142 static ExplodeInfo explodeInfo;
\r
144 /* Window class names */
\r
145 char szAppName[] = "WinBoard";
\r
146 char szConsoleName[] = "WBConsole";
\r
148 /* Title bar text */
\r
149 char szTitle[] = "WinBoard";
\r
150 char szConsoleTitle[] = "I C S Interaction";
\r
153 char *settingsFileName;
\r
154 Boolean saveSettingsOnExit;
\r
155 char installDir[MSG_SIZ];
\r
156 int errorExitStatus;
\r
158 BoardSize boardSize;
\r
159 Boolean chessProgram;
\r
160 //static int boardX, boardY;
\r
161 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
162 static int squareSize, lineGap, minorSize;
\r
163 static int winW, winH;
\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
165 static int logoHeight = 0;
\r
166 static char messageText[MESSAGE_TEXT_MAX];
\r
167 static int clockTimerEvent = 0;
\r
168 static int loadGameTimerEvent = 0;
\r
169 static int analysisTimerEvent = 0;
\r
170 static DelayedEventCallback delayedTimerCallback;
\r
171 static int delayedTimerEvent = 0;
\r
172 static int buttonCount = 2;
\r
173 char *icsTextMenuString;
\r
175 char *firstChessProgramNames;
\r
176 char *secondChessProgramNames;
\r
178 #define PALETTESIZE 256
\r
180 HINSTANCE hInst; /* current instance */
\r
181 Boolean alwaysOnTop = FALSE;
\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
184 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
186 ColorClass currentColorClass;
\r
188 HWND hCommPort = NULL; /* currently open comm port */
\r
189 static HWND hwndPause; /* pause button */
\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
192 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
193 explodeBrush, /* [HGM] atomic */
\r
194 markerBrush, /* [HGM] markers */
\r
195 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
198 static HPEN gridPen = NULL;
\r
199 static HPEN highlightPen = NULL;
\r
200 static HPEN premovePen = NULL;
\r
201 static NPLOGPALETTE pLogPal;
\r
202 static BOOL paletteChanged = FALSE;
\r
203 static HICON iconWhite, iconBlack, iconCurrent;
\r
204 static int doingSizing = FALSE;
\r
205 static int lastSizing = 0;
\r
206 static int prevStderrPort;
\r
207 static HBITMAP userLogo;
\r
209 static HBITMAP liteBackTexture = NULL;
\r
210 static HBITMAP darkBackTexture = NULL;
\r
211 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
212 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
213 static int backTextureSquareSize = 0;
\r
214 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
216 #if __GNUC__ && !defined(_winmajor)
\r
217 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
219 #if defined(_winmajor)
\r
220 #define oldDialog (_winmajor < 4)
\r
222 #define oldDialog 0
\r
232 int cliWidth, cliHeight;
\r
235 SizeInfo sizeInfo[] =
\r
237 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
238 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
239 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
240 { "petite", 33, 1, 1, 1, 0, 0 },
\r
241 { "slim", 37, 2, 1, 0, 0, 0 },
\r
242 { "small", 40, 2, 1, 0, 0, 0 },
\r
243 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
244 { "middling", 49, 2, 0, 0, 0, 0 },
\r
245 { "average", 54, 2, 0, 0, 0, 0 },
\r
246 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
247 { "medium", 64, 3, 0, 0, 0, 0 },
\r
248 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
249 { "large", 80, 3, 0, 0, 0, 0 },
\r
250 { "big", 87, 3, 0, 0, 0, 0 },
\r
251 { "huge", 95, 3, 0, 0, 0, 0 },
\r
252 { "giant", 108, 3, 0, 0, 0, 0 },
\r
253 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
254 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
255 { NULL, 0, 0, 0, 0, 0, 0 }
\r
258 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
259 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
261 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
262 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
263 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
264 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
265 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
266 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
267 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
268 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
269 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
270 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
271 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
272 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
273 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
274 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
275 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
276 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
277 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
278 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
281 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
290 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
291 #define N_BUTTONS 5
\r
293 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
295 {"<<", IDM_ToStart, NULL, NULL},
\r
296 {"<", IDM_Backward, NULL, NULL},
\r
297 {"P", IDM_Pause, NULL, NULL},
\r
298 {">", IDM_Forward, NULL, NULL},
\r
299 {">>", IDM_ToEnd, NULL, NULL},
\r
302 int tinyLayout = 0, smallLayout = 0;
\r
303 #define MENU_BAR_ITEMS 7
\r
304 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
305 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
306 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
310 MySound sounds[(int)NSoundClasses];
\r
311 MyTextAttribs textAttribs[(int)NColorClasses];
\r
313 MyColorizeAttribs colorizeAttribs[] = {
\r
314 { (COLORREF)0, 0, "Shout Text" },
\r
315 { (COLORREF)0, 0, "SShout/CShout" },
\r
316 { (COLORREF)0, 0, "Channel 1 Text" },
\r
317 { (COLORREF)0, 0, "Channel Text" },
\r
318 { (COLORREF)0, 0, "Kibitz Text" },
\r
319 { (COLORREF)0, 0, "Tell Text" },
\r
320 { (COLORREF)0, 0, "Challenge Text" },
\r
321 { (COLORREF)0, 0, "Request Text" },
\r
322 { (COLORREF)0, 0, "Seek Text" },
\r
323 { (COLORREF)0, 0, "Normal Text" },
\r
324 { (COLORREF)0, 0, "None" }
\r
329 static char *commentTitle;
\r
330 static char *commentText;
\r
331 static int commentIndex;
\r
332 static Boolean editComment = FALSE;
\r
335 char errorTitle[MSG_SIZ];
\r
336 char errorMessage[2*MSG_SIZ];
\r
337 HWND errorDialog = NULL;
\r
338 BOOLEAN moveErrorMessageUp = FALSE;
\r
339 BOOLEAN consoleEcho = TRUE;
\r
340 CHARFORMAT consoleCF;
\r
341 COLORREF consoleBackgroundColor;
\r
343 char *programVersion;
\r
349 typedef int CPKind;
\r
358 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
361 #define INPUT_SOURCE_BUF_SIZE 4096
\r
363 typedef struct _InputSource {
\r
370 char buf[INPUT_SOURCE_BUF_SIZE];
\r
374 InputCallback func;
\r
375 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
379 InputSource *consoleInputSource;
\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
385 VOID ConsoleCreate();
\r
387 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
392 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
394 void ParseIcsTextMenu(char *icsTextMenuString);
\r
395 VOID PopUpMoveDialog(char firstchar);
\r
396 VOID PopUpNameDialog(char firstchar);
\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
401 int GameListOptions();
\r
403 int dummy; // [HGM] for obsolete args
\r
405 HWND hwndMain = NULL; /* root window*/
\r
406 HWND hwndConsole = NULL;
\r
407 HWND commentDialog = NULL;
\r
408 HWND moveHistoryDialog = NULL;
\r
409 HWND evalGraphDialog = NULL;
\r
410 HWND engineOutputDialog = NULL;
\r
411 HWND gameListDialog = NULL;
\r
412 HWND editTagsDialog = NULL;
\r
414 int commentUp = FALSE;
\r
416 WindowPlacement wpMain;
\r
417 WindowPlacement wpConsole;
\r
418 WindowPlacement wpComment;
\r
419 WindowPlacement wpMoveHistory;
\r
420 WindowPlacement wpEvalGraph;
\r
421 WindowPlacement wpEngineOutput;
\r
422 WindowPlacement wpGameList;
\r
423 WindowPlacement wpTags;
\r
425 VOID EngineOptionsPopup(); // [HGM] settings
\r
427 VOID GothicPopUp(char *title, VariantClass variant);
\r
429 * Setting "frozen" should disable all user input other than deleting
\r
430 * the window. We do this while engines are initializing themselves.
\r
432 static int frozen = 0;
\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
439 if (frozen) return;
\r
441 hmenu = GetMenu(hwndMain);
\r
442 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
443 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
445 DrawMenuBar(hwndMain);
\r
448 /* Undo a FreezeUI */
\r
454 if (!frozen) return;
\r
456 hmenu = GetMenu(hwndMain);
\r
457 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
458 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
460 DrawMenuBar(hwndMain);
\r
463 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
465 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
471 #define JAWS_ALT_INTERCEPT
\r
472 #define JAWS_KB_NAVIGATION
\r
473 #define JAWS_MENU_ITEMS
\r
474 #define JAWS_SILENCE
\r
475 #define JAWS_REPLAY
\r
477 #define JAWS_COPYRIGHT
\r
478 #define JAWS_DELETE(X) X
\r
479 #define SAYMACHINEMOVE()
\r
483 /*---------------------------------------------------------------------------*\
\r
487 \*---------------------------------------------------------------------------*/
\r
490 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
491 LPSTR lpCmdLine, int nCmdShow)
\r
494 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
495 // INITCOMMONCONTROLSEX ex;
\r
499 LoadLibrary("RICHED32.DLL");
\r
500 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
502 if (!InitApplication(hInstance)) {
\r
505 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
511 // InitCommonControlsEx(&ex);
\r
512 InitCommonControls();
\r
514 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
515 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
516 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
518 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
520 while (GetMessage(&msg, /* message structure */
\r
521 NULL, /* handle of window receiving the message */
\r
522 0, /* lowest message to examine */
\r
523 0)) /* highest message to examine */
\r
526 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
527 // [HGM] navigate: switch between all windows with tab
\r
528 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
529 int i, currentElement = 0;
\r
531 // first determine what element of the chain we come from (if any)
\r
532 if(appData.icsActive) {
\r
533 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
534 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
536 if(engineOutputDialog && EngineOutputIsUp()) {
\r
537 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
538 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
540 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
541 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
543 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
544 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
545 if(msg.hwnd == e1) currentElement = 2; else
\r
546 if(msg.hwnd == e2) currentElement = 3; else
\r
547 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
548 if(msg.hwnd == mh) currentElement = 4; else
\r
549 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
550 if(msg.hwnd == hText) currentElement = 5; else
\r
551 if(msg.hwnd == hInput) currentElement = 6; else
\r
552 for (i = 0; i < N_BUTTONS; i++) {
\r
553 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
556 // determine where to go to
\r
557 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
559 currentElement = (currentElement + direction) % 7;
\r
560 switch(currentElement) {
\r
562 h = hwndMain; break; // passing this case always makes the loop exit
\r
564 h = buttonDesc[0].hwnd; break; // could be NULL
\r
566 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
569 if(!EngineOutputIsUp()) continue;
\r
572 if(!MoveHistoryIsUp()) continue;
\r
574 // case 6: // input to eval graph does not seem to get here!
\r
575 // if(!EvalGraphIsUp()) continue;
\r
576 // h = evalGraphDialog; break;
\r
578 if(!appData.icsActive) continue;
\r
582 if(!appData.icsActive) continue;
\r
588 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
589 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
592 continue; // this message now has been processed
\r
596 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
597 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
598 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
599 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
600 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
601 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
602 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
603 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
604 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
605 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
606 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
607 for(i=0; i<MAX_CHAT; i++)
\r
608 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
611 if(done) continue; // [HGM] chat: end patch
\r
612 TranslateMessage(&msg); /* Translates virtual key codes */
\r
613 DispatchMessage(&msg); /* Dispatches message to window */
\r
618 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
621 /*---------------------------------------------------------------------------*\
\r
623 * Initialization functions
\r
625 \*---------------------------------------------------------------------------*/
\r
629 { // update user logo if necessary
\r
630 static char oldUserName[MSG_SIZ], *curName;
\r
632 if(appData.autoLogo) {
\r
633 curName = UserName();
\r
634 if(strcmp(curName, oldUserName)) {
\r
635 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
636 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
637 strcpy(oldUserName, curName);
\r
643 InitApplication(HINSTANCE hInstance)
\r
647 /* Fill in window class structure with parameters that describe the */
\r
650 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
651 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
652 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
653 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
654 wc.hInstance = hInstance; /* Owner of this class */
\r
655 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
656 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
657 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
658 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
659 wc.lpszClassName = szAppName; /* Name to register as */
\r
661 /* Register the window class and return success/failure code. */
\r
662 if (!RegisterClass(&wc)) return FALSE;
\r
664 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
665 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
667 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
668 wc.hInstance = hInstance;
\r
669 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
670 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
671 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
672 wc.lpszMenuName = NULL;
\r
673 wc.lpszClassName = szConsoleName;
\r
675 if (!RegisterClass(&wc)) return FALSE;
\r
680 /* Set by InitInstance, used by EnsureOnScreen */
\r
681 int screenHeight, screenWidth;
\r
684 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
686 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
687 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
688 if (*x > screenWidth - 32) *x = 0;
\r
689 if (*y > screenHeight - 32) *y = 0;
\r
690 if (*x < minX) *x = minX;
\r
691 if (*y < minY) *y = minY;
\r
695 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
697 HWND hwnd; /* Main window handle. */
\r
699 WINDOWPLACEMENT wp;
\r
702 hInst = hInstance; /* Store instance handle in our global variable */
\r
703 programName = szAppName;
\r
705 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
706 *filepart = NULLCHAR;
\r
708 GetCurrentDirectory(MSG_SIZ, installDir);
\r
710 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
711 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
712 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
713 /* xboard, and older WinBoards, controlled the move sound with the
\r
714 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
715 always turn the option on (so that the backend will call us),
\r
716 then let the user turn the sound off by setting it to silence if
\r
717 desired. To accommodate old winboard.ini files saved by old
\r
718 versions of WinBoard, we also turn off the sound if the option
\r
719 was initially set to false. [HGM] taken out of InitAppData */
\r
720 if (!appData.ringBellAfterMoves) {
\r
721 sounds[(int)SoundMove].name = strdup("");
\r
722 appData.ringBellAfterMoves = TRUE;
\r
724 if (appData.debugMode) {
\r
725 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
726 setbuf(debugFP, NULL);
\r
731 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
732 // InitEngineUCI( installDir, &second );
\r
734 /* Create a main window for this application instance. */
\r
735 hwnd = CreateWindow(szAppName, szTitle,
\r
736 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
737 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
738 NULL, NULL, hInstance, NULL);
\r
741 /* If window could not be created, return "failure" */
\r
746 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
747 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
748 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
750 if (first.programLogo == NULL && appData.debugMode) {
\r
751 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
753 } else if(appData.autoLogo) {
\r
754 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
756 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
757 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
761 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
762 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
764 if (second.programLogo == NULL && appData.debugMode) {
\r
765 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
767 } else if(appData.autoLogo) {
\r
769 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
770 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
771 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
773 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
774 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
775 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
781 iconWhite = LoadIcon(hInstance, "icon_white");
\r
782 iconBlack = LoadIcon(hInstance, "icon_black");
\r
783 iconCurrent = iconWhite;
\r
784 InitDrawingColors();
\r
785 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
786 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
787 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
788 /* Compute window size for each board size, and use the largest
\r
789 size that fits on this screen as the default. */
\r
790 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
791 if (boardSize == (BoardSize)-1 &&
\r
792 winH <= screenHeight
\r
793 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
794 && winW <= screenWidth) {
\r
795 boardSize = (BoardSize)ibs;
\r
799 InitDrawingSizes(boardSize, 0);
\r
801 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
803 /* [AS] Load textures if specified */
\r
804 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
806 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
807 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
808 liteBackTextureMode = appData.liteBackTextureMode;
\r
810 if (liteBackTexture == NULL && appData.debugMode) {
\r
811 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
815 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
816 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
817 darkBackTextureMode = appData.darkBackTextureMode;
\r
819 if (darkBackTexture == NULL && appData.debugMode) {
\r
820 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
824 mysrandom( (unsigned) time(NULL) );
\r
826 /* [AS] Restore layout */
\r
827 if( wpMoveHistory.visible ) {
\r
828 MoveHistoryPopUp();
\r
831 if( wpEvalGraph.visible ) {
\r
835 if( wpEngineOutput.visible ) {
\r
836 EngineOutputPopUp();
\r
841 /* Make the window visible; update its client area; and return "success" */
\r
842 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
843 wp.length = sizeof(WINDOWPLACEMENT);
\r
845 wp.showCmd = nCmdShow;
\r
846 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
847 wp.rcNormalPosition.left = wpMain.x;
\r
848 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
849 wp.rcNormalPosition.top = wpMain.y;
\r
850 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
851 SetWindowPlacement(hwndMain, &wp);
\r
853 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
854 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
858 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
859 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
861 ShowWindow(hwndConsole, nCmdShow);
\r
863 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
864 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
873 HMENU hmenu = GetMenu(hwndMain);
\r
875 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
876 MF_BYCOMMAND|((appData.icsActive &&
\r
877 *appData.icsCommPort != NULLCHAR) ?
\r
878 MF_ENABLED : MF_GRAYED));
\r
879 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
880 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
881 MF_CHECKED : MF_UNCHECKED));
\r
884 //---------------------------------------------------------------------------------------------------------
\r
886 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
887 #define XBOARD FALSE
\r
889 #define OPTCHAR "/"
\r
890 #define SEPCHAR "="
\r
894 // front-end part of option handling
\r
897 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
899 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
900 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
903 lf->lfEscapement = 0;
\r
904 lf->lfOrientation = 0;
\r
905 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
906 lf->lfItalic = mfp->italic;
\r
907 lf->lfUnderline = mfp->underline;
\r
908 lf->lfStrikeOut = mfp->strikeout;
\r
909 lf->lfCharSet = mfp->charset;
\r
910 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
911 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
912 lf->lfQuality = DEFAULT_QUALITY;
\r
913 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
914 strcpy(lf->lfFaceName, mfp->faceName);
\r
918 CreateFontInMF(MyFont *mf)
\r
920 LFfromMFP(&mf->lf, &mf->mfp);
\r
921 if (mf->hf) DeleteObject(mf->hf);
\r
922 mf->hf = CreateFontIndirect(&mf->lf);
\r
925 // [HGM] This platform-dependent table provides the location for storing the color info
\r
927 colorVariable[] = {
\r
932 &highlightSquareColor,
\r
933 &premoveHighlightColor,
\r
935 &consoleBackgroundColor,
\r
936 &appData.fontForeColorWhite,
\r
937 &appData.fontBackColorWhite,
\r
938 &appData.fontForeColorBlack,
\r
939 &appData.fontBackColorBlack,
\r
940 &appData.evalHistColorWhite,
\r
941 &appData.evalHistColorBlack,
\r
942 &appData.highlightArrowColor,
\r
945 /* Command line font name parser. NULL name means do nothing.
\r
946 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
947 For backward compatibility, syntax without the colon is also
\r
948 accepted, but font names with digits in them won't work in that case.
\r
951 ParseFontName(char *name, MyFontParams *mfp)
\r
954 if (name == NULL) return;
\r
956 q = strchr(p, ':');
\r
958 if (q - p >= sizeof(mfp->faceName))
\r
959 ExitArgError("Font name too long:", name);
\r
960 memcpy(mfp->faceName, p, q - p);
\r
961 mfp->faceName[q - p] = NULLCHAR;
\r
965 while (*p && !isdigit(*p)) {
\r
967 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
968 ExitArgError("Font name too long:", name);
\r
970 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
973 if (!*p) ExitArgError("Font point size missing:", name);
\r
974 mfp->pointSize = (float) atof(p);
\r
975 mfp->bold = (strchr(p, 'b') != NULL);
\r
976 mfp->italic = (strchr(p, 'i') != NULL);
\r
977 mfp->underline = (strchr(p, 'u') != NULL);
\r
978 mfp->strikeout = (strchr(p, 's') != NULL);
\r
979 mfp->charset = DEFAULT_CHARSET;
\r
980 q = strchr(p, 'c');
\r
982 mfp->charset = (BYTE) atoi(q+1);
\r
986 ParseFont(char *name, int number)
\r
987 { // wrapper to shield back-end from 'font'
\r
988 ParseFontName(name, &font[boardSize][number]->mfp);
\r
993 { // in WB we have a 2D array of fonts; this initializes their description
\r
995 /* Point font array elements to structures and
\r
996 parse default font names */
\r
997 for (i=0; i<NUM_FONTS; i++) {
\r
998 for (j=0; j<NUM_SIZES; j++) {
\r
999 font[j][i] = &fontRec[j][i];
\r
1000 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1007 { // here we create the actual fonts from the selected descriptions
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 CreateFontInMF(font[j][i]);
\r
1015 /* Color name parser.
\r
1016 X version accepts X color names, but this one
\r
1017 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1019 ParseColorName(char *name)
\r
1021 int red, green, blue, count;
\r
1022 char buf[MSG_SIZ];
\r
1024 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1026 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1027 &red, &green, &blue);
\r
1030 sprintf(buf, "Can't parse color name %s", name);
\r
1031 DisplayError(buf, 0);
\r
1032 return RGB(0, 0, 0);
\r
1034 return PALETTERGB(red, green, blue);
\r
1038 ParseColor(int n, char *name)
\r
1039 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1040 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1044 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1046 char *e = argValue;
\r
1050 if (*e == 'b') eff |= CFE_BOLD;
\r
1051 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1052 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1053 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1054 else if (*e == '#' || isdigit(*e)) break;
\r
1058 *color = ParseColorName(e);
\r
1062 ParseTextAttribs(ColorClass cc, char *s)
\r
1063 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1064 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1065 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1069 ParseBoardSize(void *addr, char *name)
\r
1070 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1071 BoardSize bs = SizeTiny;
\r
1072 while (sizeInfo[bs].name != NULL) {
\r
1073 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1074 *(BoardSize *)addr = bs;
\r
1079 ExitArgError("Unrecognized board size value", name);
\r
1084 { // [HGM] import name from appData first
\r
1087 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1088 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1089 textAttribs[cc].sound.data = NULL;
\r
1090 MyLoadSound(&textAttribs[cc].sound);
\r
1092 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1093 textAttribs[cc].sound.name = strdup("");
\r
1094 textAttribs[cc].sound.data = NULL;
\r
1096 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1097 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1098 sounds[sc].data = NULL;
\r
1099 MyLoadSound(&sounds[sc]);
\r
1104 SetCommPortDefaults()
\r
1106 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1107 dcb.DCBlength = sizeof(DCB);
\r
1108 dcb.BaudRate = 9600;
\r
1109 dcb.fBinary = TRUE;
\r
1110 dcb.fParity = FALSE;
\r
1111 dcb.fOutxCtsFlow = FALSE;
\r
1112 dcb.fOutxDsrFlow = FALSE;
\r
1113 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1114 dcb.fDsrSensitivity = FALSE;
\r
1115 dcb.fTXContinueOnXoff = TRUE;
\r
1116 dcb.fOutX = FALSE;
\r
1118 dcb.fNull = FALSE;
\r
1119 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1120 dcb.fAbortOnError = FALSE;
\r
1122 dcb.Parity = SPACEPARITY;
\r
1123 dcb.StopBits = ONESTOPBIT;
\r
1126 // [HGM] args: these three cases taken out to stay in front-end
\r
1128 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1129 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1130 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1131 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1133 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1134 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1135 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1136 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1137 ad->argName, mfp->faceName, mfp->pointSize,
\r
1138 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1139 mfp->bold ? "b" : "",
\r
1140 mfp->italic ? "i" : "",
\r
1141 mfp->underline ? "u" : "",
\r
1142 mfp->strikeout ? "s" : "",
\r
1143 (int)mfp->charset);
\r
1149 { // [HGM] copy the names from the internal WB variables to appData
\r
1152 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1153 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1154 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1155 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1159 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1160 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1161 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1162 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1163 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1164 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1165 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1166 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1167 (ta->effects) ? " " : "",
\r
1168 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1172 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1173 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1174 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1175 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1176 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1180 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1181 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1182 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1186 ParseCommPortSettings(char *s)
\r
1187 { // wrapper to keep dcb from back-end
\r
1188 ParseCommSettings(s, &dcb);
\r
1193 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1194 GetActualPlacement(hwndMain, &wpMain);
\r
1195 GetActualPlacement(hwndConsole, &wpConsole);
\r
1196 GetActualPlacement(commentDialog, &wpComment);
\r
1197 GetActualPlacement(editTagsDialog, &wpTags);
\r
1198 GetActualPlacement(gameListDialog, &wpGameList);
\r
1199 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1200 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1201 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1205 PrintCommPortSettings(FILE *f, char *name)
\r
1206 { // wrapper to shield back-end from DCB
\r
1207 PrintCommSettings(f, name, &dcb);
\r
1211 MySearchPath(char *installDir, char *name, char *fullname)
\r
1214 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1218 MyGetFullPathName(char *name, char *fullname)
\r
1221 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1226 { // [HGM] args: allows testing if main window is realized from back-end
\r
1227 return hwndMain != NULL;
\r
1231 PopUpStartupDialog()
\r
1235 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1236 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1237 FreeProcInstance(lpProc);
\r
1240 /*---------------------------------------------------------------------------*\
\r
1242 * GDI board drawing routines
\r
1244 \*---------------------------------------------------------------------------*/
\r
1246 /* [AS] Draw square using background texture */
\r
1247 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1252 return; /* Should never happen! */
\r
1255 SetGraphicsMode( dst, GM_ADVANCED );
\r
1262 /* X reflection */
\r
1267 x.eDx = (FLOAT) dw + dx - 1;
\r
1270 SetWorldTransform( dst, &x );
\r
1273 /* Y reflection */
\r
1279 x.eDy = (FLOAT) dh + dy - 1;
\r
1281 SetWorldTransform( dst, &x );
\r
1289 x.eDx = (FLOAT) dx;
\r
1290 x.eDy = (FLOAT) dy;
\r
1293 SetWorldTransform( dst, &x );
\r
1297 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1305 SetWorldTransform( dst, &x );
\r
1307 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1310 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1312 PM_WP = (int) WhitePawn,
\r
1313 PM_WN = (int) WhiteKnight,
\r
1314 PM_WB = (int) WhiteBishop,
\r
1315 PM_WR = (int) WhiteRook,
\r
1316 PM_WQ = (int) WhiteQueen,
\r
1317 PM_WF = (int) WhiteFerz,
\r
1318 PM_WW = (int) WhiteWazir,
\r
1319 PM_WE = (int) WhiteAlfil,
\r
1320 PM_WM = (int) WhiteMan,
\r
1321 PM_WO = (int) WhiteCannon,
\r
1322 PM_WU = (int) WhiteUnicorn,
\r
1323 PM_WH = (int) WhiteNightrider,
\r
1324 PM_WA = (int) WhiteAngel,
\r
1325 PM_WC = (int) WhiteMarshall,
\r
1326 PM_WAB = (int) WhiteCardinal,
\r
1327 PM_WD = (int) WhiteDragon,
\r
1328 PM_WL = (int) WhiteLance,
\r
1329 PM_WS = (int) WhiteCobra,
\r
1330 PM_WV = (int) WhiteFalcon,
\r
1331 PM_WSG = (int) WhiteSilver,
\r
1332 PM_WG = (int) WhiteGrasshopper,
\r
1333 PM_WK = (int) WhiteKing,
\r
1334 PM_BP = (int) BlackPawn,
\r
1335 PM_BN = (int) BlackKnight,
\r
1336 PM_BB = (int) BlackBishop,
\r
1337 PM_BR = (int) BlackRook,
\r
1338 PM_BQ = (int) BlackQueen,
\r
1339 PM_BF = (int) BlackFerz,
\r
1340 PM_BW = (int) BlackWazir,
\r
1341 PM_BE = (int) BlackAlfil,
\r
1342 PM_BM = (int) BlackMan,
\r
1343 PM_BO = (int) BlackCannon,
\r
1344 PM_BU = (int) BlackUnicorn,
\r
1345 PM_BH = (int) BlackNightrider,
\r
1346 PM_BA = (int) BlackAngel,
\r
1347 PM_BC = (int) BlackMarshall,
\r
1348 PM_BG = (int) BlackGrasshopper,
\r
1349 PM_BAB = (int) BlackCardinal,
\r
1350 PM_BD = (int) BlackDragon,
\r
1351 PM_BL = (int) BlackLance,
\r
1352 PM_BS = (int) BlackCobra,
\r
1353 PM_BV = (int) BlackFalcon,
\r
1354 PM_BSG = (int) BlackSilver,
\r
1355 PM_BK = (int) BlackKing
\r
1358 static HFONT hPieceFont = NULL;
\r
1359 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1360 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1361 static int fontBitmapSquareSize = 0;
\r
1362 static char pieceToFontChar[(int) EmptySquare] =
\r
1363 { 'p', 'n', 'b', 'r', 'q',
\r
1364 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1365 'k', 'o', 'm', 'v', 't', 'w',
\r
1366 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1369 extern BOOL SetCharTable( char *table, const char * map );
\r
1370 /* [HGM] moved to backend.c */
\r
1372 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1375 BYTE r1 = GetRValue( color );
\r
1376 BYTE g1 = GetGValue( color );
\r
1377 BYTE b1 = GetBValue( color );
\r
1383 /* Create a uniform background first */
\r
1384 hbrush = CreateSolidBrush( color );
\r
1385 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1386 FillRect( hdc, &rc, hbrush );
\r
1387 DeleteObject( hbrush );
\r
1390 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1391 int steps = squareSize / 2;
\r
1394 for( i=0; i<steps; i++ ) {
\r
1395 BYTE r = r1 - (r1-r2) * i / steps;
\r
1396 BYTE g = g1 - (g1-g2) * i / steps;
\r
1397 BYTE b = b1 - (b1-b2) * i / steps;
\r
1399 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1400 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1401 FillRect( hdc, &rc, hbrush );
\r
1402 DeleteObject(hbrush);
\r
1405 else if( mode == 2 ) {
\r
1406 /* Diagonal gradient, good more or less for every piece */
\r
1407 POINT triangle[3];
\r
1408 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1409 HBRUSH hbrush_old;
\r
1410 int steps = squareSize;
\r
1413 triangle[0].x = squareSize - steps;
\r
1414 triangle[0].y = squareSize;
\r
1415 triangle[1].x = squareSize;
\r
1416 triangle[1].y = squareSize;
\r
1417 triangle[2].x = squareSize;
\r
1418 triangle[2].y = squareSize - steps;
\r
1420 for( i=0; i<steps; i++ ) {
\r
1421 BYTE r = r1 - (r1-r2) * i / steps;
\r
1422 BYTE g = g1 - (g1-g2) * i / steps;
\r
1423 BYTE b = b1 - (b1-b2) * i / steps;
\r
1425 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1426 hbrush_old = SelectObject( hdc, hbrush );
\r
1427 Polygon( hdc, triangle, 3 );
\r
1428 SelectObject( hdc, hbrush_old );
\r
1429 DeleteObject(hbrush);
\r
1434 SelectObject( hdc, hpen );
\r
1439 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1440 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1441 piece: follow the steps as explained below.
\r
1443 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1447 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1451 int backColor = whitePieceColor;
\r
1452 int foreColor = blackPieceColor;
\r
1454 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1455 backColor = appData.fontBackColorWhite;
\r
1456 foreColor = appData.fontForeColorWhite;
\r
1458 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1459 backColor = appData.fontBackColorBlack;
\r
1460 foreColor = appData.fontForeColorBlack;
\r
1464 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1466 hbm_old = SelectObject( hdc, hbm );
\r
1470 rc.right = squareSize;
\r
1471 rc.bottom = squareSize;
\r
1473 /* Step 1: background is now black */
\r
1474 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1476 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1478 pt.x = (squareSize - sz.cx) / 2;
\r
1479 pt.y = (squareSize - sz.cy) / 2;
\r
1481 SetBkMode( hdc, TRANSPARENT );
\r
1482 SetTextColor( hdc, chroma );
\r
1483 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1484 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1486 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1487 /* Step 3: the area outside the piece is filled with white */
\r
1488 // FloodFill( hdc, 0, 0, chroma );
\r
1489 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1490 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1491 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1492 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1493 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1495 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1496 but if the start point is not inside the piece we're lost!
\r
1497 There should be a better way to do this... if we could create a region or path
\r
1498 from the fill operation we would be fine for example.
\r
1500 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1501 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1503 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1504 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1505 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1507 SelectObject( dc2, bm2 );
\r
1508 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1509 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1510 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1511 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1512 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1515 DeleteObject( bm2 );
\r
1518 SetTextColor( hdc, 0 );
\r
1520 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1521 draw the piece again in black for safety.
\r
1523 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1525 SelectObject( hdc, hbm_old );
\r
1527 if( hPieceMask[index] != NULL ) {
\r
1528 DeleteObject( hPieceMask[index] );
\r
1531 hPieceMask[index] = hbm;
\r
1534 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1536 SelectObject( hdc, hbm );
\r
1539 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1540 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1541 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1543 SelectObject( dc1, hPieceMask[index] );
\r
1544 SelectObject( dc2, bm2 );
\r
1545 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1546 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1549 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1550 the piece background and deletes (makes transparent) the rest.
\r
1551 Thanks to that mask, we are free to paint the background with the greates
\r
1552 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1553 We use this, to make gradients and give the pieces a "roundish" look.
\r
1555 SetPieceBackground( hdc, backColor, 2 );
\r
1556 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1560 DeleteObject( bm2 );
\r
1563 SetTextColor( hdc, foreColor );
\r
1564 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1566 SelectObject( hdc, hbm_old );
\r
1568 if( hPieceFace[index] != NULL ) {
\r
1569 DeleteObject( hPieceFace[index] );
\r
1572 hPieceFace[index] = hbm;
\r
1575 static int TranslatePieceToFontPiece( int piece )
\r
1605 case BlackMarshall:
\r
1609 case BlackNightrider:
\r
1615 case BlackUnicorn:
\r
1619 case BlackGrasshopper:
\r
1631 case BlackCardinal:
\r
1638 case WhiteMarshall:
\r
1642 case WhiteNightrider:
\r
1648 case WhiteUnicorn:
\r
1652 case WhiteGrasshopper:
\r
1664 case WhiteCardinal:
\r
1673 void CreatePiecesFromFont()
\r
1676 HDC hdc_window = NULL;
\r
1682 if( fontBitmapSquareSize < 0 ) {
\r
1683 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1687 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1688 fontBitmapSquareSize = -1;
\r
1692 if( fontBitmapSquareSize != squareSize ) {
\r
1693 hdc_window = GetDC( hwndMain );
\r
1694 hdc = CreateCompatibleDC( hdc_window );
\r
1696 if( hPieceFont != NULL ) {
\r
1697 DeleteObject( hPieceFont );
\r
1700 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1701 hPieceMask[i] = NULL;
\r
1702 hPieceFace[i] = NULL;
\r
1708 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1709 fontHeight = appData.fontPieceSize;
\r
1712 fontHeight = (fontHeight * squareSize) / 100;
\r
1714 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1716 lf.lfEscapement = 0;
\r
1717 lf.lfOrientation = 0;
\r
1718 lf.lfWeight = FW_NORMAL;
\r
1720 lf.lfUnderline = 0;
\r
1721 lf.lfStrikeOut = 0;
\r
1722 lf.lfCharSet = DEFAULT_CHARSET;
\r
1723 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1724 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1725 lf.lfQuality = PROOF_QUALITY;
\r
1726 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1727 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1728 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1730 hPieceFont = CreateFontIndirect( &lf );
\r
1732 if( hPieceFont == NULL ) {
\r
1733 fontBitmapSquareSize = -2;
\r
1736 /* Setup font-to-piece character table */
\r
1737 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1738 /* No (or wrong) global settings, try to detect the font */
\r
1739 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1741 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1743 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1744 /* DiagramTT* family */
\r
1745 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1747 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1748 /* Fairy symbols */
\r
1749 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1751 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1752 /* Good Companion (Some characters get warped as literal :-( */
\r
1753 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1754 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1755 SetCharTable(pieceToFontChar, s);
\r
1758 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1759 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1763 /* Create bitmaps */
\r
1764 hfont_old = SelectObject( hdc, hPieceFont );
\r
1765 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1766 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1767 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1769 SelectObject( hdc, hfont_old );
\r
1771 fontBitmapSquareSize = squareSize;
\r
1775 if( hdc != NULL ) {
\r
1779 if( hdc_window != NULL ) {
\r
1780 ReleaseDC( hwndMain, hdc_window );
\r
1785 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1789 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1790 if (gameInfo.event &&
\r
1791 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1792 strcmp(name, "k80s") == 0) {
\r
1793 strcpy(name, "tim");
\r
1795 return LoadBitmap(hinst, name);
\r
1799 /* Insert a color into the program's logical palette
\r
1800 structure. This code assumes the given color is
\r
1801 the result of the RGB or PALETTERGB macro, and it
\r
1802 knows how those macros work (which is documented).
\r
1805 InsertInPalette(COLORREF color)
\r
1807 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1809 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1810 DisplayFatalError("Too many colors", 0, 1);
\r
1811 pLogPal->palNumEntries--;
\r
1815 pe->peFlags = (char) 0;
\r
1816 pe->peRed = (char) (0xFF & color);
\r
1817 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1818 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1824 InitDrawingColors()
\r
1826 if (pLogPal == NULL) {
\r
1827 /* Allocate enough memory for a logical palette with
\r
1828 * PALETTESIZE entries and set the size and version fields
\r
1829 * of the logical palette structure.
\r
1831 pLogPal = (NPLOGPALETTE)
\r
1832 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1833 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1834 pLogPal->palVersion = 0x300;
\r
1836 pLogPal->palNumEntries = 0;
\r
1838 InsertInPalette(lightSquareColor);
\r
1839 InsertInPalette(darkSquareColor);
\r
1840 InsertInPalette(whitePieceColor);
\r
1841 InsertInPalette(blackPieceColor);
\r
1842 InsertInPalette(highlightSquareColor);
\r
1843 InsertInPalette(premoveHighlightColor);
\r
1845 /* create a logical color palette according the information
\r
1846 * in the LOGPALETTE structure.
\r
1848 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1850 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1851 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1852 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1853 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1854 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1855 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1856 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1857 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1858 /* [AS] Force rendering of the font-based pieces */
\r
1859 if( fontBitmapSquareSize > 0 ) {
\r
1860 fontBitmapSquareSize = 0;
\r
1866 BoardWidth(int boardSize, int n)
\r
1867 { /* [HGM] argument n added to allow different width and height */
\r
1868 int lineGap = sizeInfo[boardSize].lineGap;
\r
1870 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1871 lineGap = appData.overrideLineGap;
\r
1874 return (n + 1) * lineGap +
\r
1875 n * sizeInfo[boardSize].squareSize;
\r
1878 /* Respond to board resize by dragging edge */
\r
1880 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1882 BoardSize newSize = NUM_SIZES - 1;
\r
1883 static int recurse = 0;
\r
1884 if (IsIconic(hwndMain)) return;
\r
1885 if (recurse > 0) return;
\r
1887 while (newSize > 0) {
\r
1888 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1889 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1890 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1893 boardSize = newSize;
\r
1894 InitDrawingSizes(boardSize, flags);
\r
1901 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1903 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1904 ChessSquare piece;
\r
1905 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1907 SIZE clockSize, messageSize;
\r
1909 char buf[MSG_SIZ];
\r
1911 HMENU hmenu = GetMenu(hwndMain);
\r
1912 RECT crect, wrect, oldRect;
\r
1914 LOGBRUSH logbrush;
\r
1916 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1917 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1919 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1920 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1922 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1923 oldRect.top = wpMain.y;
\r
1924 oldRect.right = wpMain.x + wpMain.width;
\r
1925 oldRect.bottom = wpMain.y + wpMain.height;
\r
1927 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1928 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1929 squareSize = sizeInfo[boardSize].squareSize;
\r
1930 lineGap = sizeInfo[boardSize].lineGap;
\r
1931 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1933 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1934 lineGap = appData.overrideLineGap;
\r
1937 if (tinyLayout != oldTinyLayout) {
\r
1938 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1940 style &= ~WS_SYSMENU;
\r
1941 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1942 "&Minimize\tCtrl+F4");
\r
1944 style |= WS_SYSMENU;
\r
1945 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1947 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1949 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1950 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1951 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1953 DrawMenuBar(hwndMain);
\r
1956 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1957 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1959 /* Get text area sizes */
\r
1960 hdc = GetDC(hwndMain);
\r
1961 if (appData.clockMode) {
\r
1962 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1964 sprintf(buf, "White");
\r
1966 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1967 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1968 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1969 str = "We only care about the height here";
\r
1970 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1971 SelectObject(hdc, oldFont);
\r
1972 ReleaseDC(hwndMain, hdc);
\r
1974 /* Compute where everything goes */
\r
1975 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1976 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1977 logoHeight = 2*clockSize.cy;
\r
1978 leftLogoRect.left = OUTER_MARGIN;
\r
1979 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1980 leftLogoRect.top = OUTER_MARGIN;
\r
1981 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1983 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1984 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1985 rightLogoRect.top = OUTER_MARGIN;
\r
1986 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1989 whiteRect.left = leftLogoRect.right;
\r
1990 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
1991 whiteRect.top = OUTER_MARGIN;
\r
1992 whiteRect.bottom = whiteRect.top + logoHeight;
\r
1994 blackRect.right = rightLogoRect.left;
\r
1995 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
1996 blackRect.top = whiteRect.top;
\r
1997 blackRect.bottom = whiteRect.bottom;
\r
1999 whiteRect.left = OUTER_MARGIN;
\r
2000 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2001 whiteRect.top = OUTER_MARGIN;
\r
2002 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2004 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2005 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2006 blackRect.top = whiteRect.top;
\r
2007 blackRect.bottom = whiteRect.bottom;
\r
2010 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2011 if (appData.showButtonBar) {
\r
2012 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2013 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2015 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2017 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2018 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2020 boardRect.left = OUTER_MARGIN;
\r
2021 boardRect.right = boardRect.left + boardWidth;
\r
2022 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2023 boardRect.bottom = boardRect.top + boardHeight;
\r
2025 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2026 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2027 oldBoardSize = boardSize;
\r
2028 oldTinyLayout = tinyLayout;
\r
2029 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2030 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2031 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2032 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2033 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2034 wpMain.height = winH; // without disturbing window attachments
\r
2035 GetWindowRect(hwndMain, &wrect);
\r
2036 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2037 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2039 // [HGM] placement: let attached windows follow size change.
\r
2040 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2041 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2042 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2043 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2044 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2046 /* compensate if menu bar wrapped */
\r
2047 GetClientRect(hwndMain, &crect);
\r
2048 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2049 wpMain.height += offby;
\r
2051 case WMSZ_TOPLEFT:
\r
2052 SetWindowPos(hwndMain, NULL,
\r
2053 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2054 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2057 case WMSZ_TOPRIGHT:
\r
2059 SetWindowPos(hwndMain, NULL,
\r
2060 wrect.left, wrect.bottom - wpMain.height,
\r
2061 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2064 case WMSZ_BOTTOMLEFT:
\r
2066 SetWindowPos(hwndMain, NULL,
\r
2067 wrect.right - wpMain.width, wrect.top,
\r
2068 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2071 case WMSZ_BOTTOMRIGHT:
\r
2075 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2076 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2081 for (i = 0; i < N_BUTTONS; i++) {
\r
2082 if (buttonDesc[i].hwnd != NULL) {
\r
2083 DestroyWindow(buttonDesc[i].hwnd);
\r
2084 buttonDesc[i].hwnd = NULL;
\r
2086 if (appData.showButtonBar) {
\r
2087 buttonDesc[i].hwnd =
\r
2088 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2089 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2090 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2091 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2092 (HMENU) buttonDesc[i].id,
\r
2093 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2095 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2096 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2097 MAKELPARAM(FALSE, 0));
\r
2099 if (buttonDesc[i].id == IDM_Pause)
\r
2100 hwndPause = buttonDesc[i].hwnd;
\r
2101 buttonDesc[i].wndproc = (WNDPROC)
\r
2102 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2105 if (gridPen != NULL) DeleteObject(gridPen);
\r
2106 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2107 if (premovePen != NULL) DeleteObject(premovePen);
\r
2108 if (lineGap != 0) {
\r
2109 logbrush.lbStyle = BS_SOLID;
\r
2110 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2112 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2113 lineGap, &logbrush, 0, NULL);
\r
2114 logbrush.lbColor = highlightSquareColor;
\r
2116 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2117 lineGap, &logbrush, 0, NULL);
\r
2119 logbrush.lbColor = premoveHighlightColor;
\r
2121 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2122 lineGap, &logbrush, 0, NULL);
\r
2124 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2125 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2126 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2127 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2128 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2129 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2130 BOARD_WIDTH * (squareSize + lineGap);
\r
2131 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2133 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2134 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2135 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2136 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2137 lineGap / 2 + (i * (squareSize + lineGap));
\r
2138 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2139 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2140 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2144 /* [HGM] Licensing requirement */
\r
2146 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2149 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2151 GothicPopUp( "", VariantNormal);
\r
2154 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2156 /* Load piece bitmaps for this board size */
\r
2157 for (i=0; i<=2; i++) {
\r
2158 for (piece = WhitePawn;
\r
2159 (int) piece < (int) BlackPawn;
\r
2160 piece = (ChessSquare) ((int) piece + 1)) {
\r
2161 if (pieceBitmap[i][piece] != NULL)
\r
2162 DeleteObject(pieceBitmap[i][piece]);
\r
2166 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2167 // Orthodox Chess pieces
\r
2168 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2169 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2170 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2171 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2172 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2173 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2174 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2175 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2176 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2177 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2178 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2179 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2180 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2181 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2182 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2183 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2184 // in Shogi, Hijack the unused Queen for Lance
\r
2185 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2186 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2187 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2189 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2190 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2191 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2194 if(squareSize <= 72 && squareSize >= 33) {
\r
2195 /* A & C are available in most sizes now */
\r
2196 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2197 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2198 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2199 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2200 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2201 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2202 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2203 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2204 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2205 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2206 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2207 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2208 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2209 } else { // Smirf-like
\r
2210 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2211 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2212 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2214 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2215 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2216 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2217 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2218 } else { // WinBoard standard
\r
2219 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2220 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2221 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2226 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2227 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2228 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2229 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2230 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2231 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2232 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2233 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2236 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2237 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2238 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2239 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2240 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2241 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2242 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2245 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2246 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2247 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2248 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2249 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2250 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2251 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2252 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2253 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2254 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2255 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2256 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2258 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2259 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2272 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2273 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2274 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2275 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2276 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2277 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2278 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2279 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2280 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2281 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2282 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2283 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2286 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2287 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2288 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2289 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2290 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2291 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2292 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2293 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2294 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2295 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2296 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2297 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2298 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2299 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2300 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2304 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2305 /* special Shogi support in this size */
\r
2306 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2307 for (piece = WhitePawn;
\r
2308 (int) piece < (int) BlackPawn;
\r
2309 piece = (ChessSquare) ((int) piece + 1)) {
\r
2310 if (pieceBitmap[i][piece] != NULL)
\r
2311 DeleteObject(pieceBitmap[i][piece]);
\r
2314 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2315 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2316 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2317 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2318 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2319 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2320 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2321 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2322 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2323 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2324 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2325 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2326 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2327 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2328 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2329 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2330 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2331 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2332 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2333 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2334 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2335 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2336 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2337 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2338 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2339 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2340 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2341 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2342 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2343 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2344 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2345 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2346 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2347 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2348 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2349 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2350 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2351 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2352 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2353 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2354 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2355 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2361 PieceBitmap(ChessSquare p, int kind)
\r
2363 if ((int) p >= (int) BlackPawn)
\r
2364 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2366 return pieceBitmap[kind][(int) p];
\r
2369 /***************************************************************/
\r
2371 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2372 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2374 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2375 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2379 SquareToPos(int row, int column, int * x, int * y)
\r
2382 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2383 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2385 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2386 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2391 DrawCoordsOnDC(HDC hdc)
\r
2393 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2394 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2395 char str[2] = { NULLCHAR, NULLCHAR };
\r
2396 int oldMode, oldAlign, x, y, start, i;
\r
2400 if (!appData.showCoords)
\r
2403 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2405 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2406 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2407 oldAlign = GetTextAlign(hdc);
\r
2408 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2410 y = boardRect.top + lineGap;
\r
2411 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2413 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2414 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2415 str[0] = files[start + i];
\r
2416 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2417 y += squareSize + lineGap;
\r
2420 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2422 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2423 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2424 str[0] = ranks[start + i];
\r
2425 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2426 x += squareSize + lineGap;
\r
2429 SelectObject(hdc, oldBrush);
\r
2430 SetBkMode(hdc, oldMode);
\r
2431 SetTextAlign(hdc, oldAlign);
\r
2432 SelectObject(hdc, oldFont);
\r
2436 DrawGridOnDC(HDC hdc)
\r
2440 if (lineGap != 0) {
\r
2441 oldPen = SelectObject(hdc, gridPen);
\r
2442 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2443 SelectObject(hdc, oldPen);
\r
2447 #define HIGHLIGHT_PEN 0
\r
2448 #define PREMOVE_PEN 1
\r
2451 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2454 HPEN oldPen, hPen;
\r
2455 if (lineGap == 0) return;
\r
2457 x1 = boardRect.left +
\r
2458 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2459 y1 = boardRect.top +
\r
2460 lineGap/2 + y * (squareSize + lineGap);
\r
2462 x1 = boardRect.left +
\r
2463 lineGap/2 + x * (squareSize + lineGap);
\r
2464 y1 = boardRect.top +
\r
2465 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2467 hPen = pen ? premovePen : highlightPen;
\r
2468 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2469 MoveToEx(hdc, x1, y1, NULL);
\r
2470 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2471 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2472 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2473 LineTo(hdc, x1, y1);
\r
2474 SelectObject(hdc, oldPen);
\r
2478 DrawHighlightsOnDC(HDC hdc)
\r
2481 for (i=0; i<2; i++) {
\r
2482 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2483 DrawHighlightOnDC(hdc, TRUE,
\r
2484 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2487 for (i=0; i<2; i++) {
\r
2488 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2489 premoveHighlightInfo.sq[i].y >= 0) {
\r
2490 DrawHighlightOnDC(hdc, TRUE,
\r
2491 premoveHighlightInfo.sq[i].x,
\r
2492 premoveHighlightInfo.sq[i].y,
\r
2498 /* Note: sqcolor is used only in monoMode */
\r
2499 /* Note that this code is largely duplicated in woptions.c,
\r
2500 function DrawSampleSquare, so that needs to be updated too */
\r
2502 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2504 HBITMAP oldBitmap;
\r
2508 if (appData.blindfold) return;
\r
2510 /* [AS] Use font-based pieces if needed */
\r
2511 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2512 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2513 CreatePiecesFromFont();
\r
2515 if( fontBitmapSquareSize == squareSize ) {
\r
2516 int index = TranslatePieceToFontPiece(piece);
\r
2518 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2522 squareSize, squareSize,
\r
2527 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2531 squareSize, squareSize,
\r
2540 if (appData.monoMode) {
\r
2541 SelectObject(tmphdc, PieceBitmap(piece,
\r
2542 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2543 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2544 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2546 tmpSize = squareSize;
\r
2548 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2549 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2550 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2551 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2552 x += (squareSize - minorSize)>>1;
\r
2553 y += squareSize - minorSize - 2;
\r
2554 tmpSize = minorSize;
\r
2556 if (color || appData.allWhite ) {
\r
2557 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2559 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2560 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2561 if(appData.upsideDown && color==flipView)
\r
2562 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2564 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2565 /* Use black for outline of white pieces */
\r
2566 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2567 if(appData.upsideDown && color==flipView)
\r
2568 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2570 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2572 /* Use square color for details of black pieces */
\r
2573 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2574 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2575 if(appData.upsideDown && !flipView)
\r
2576 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2578 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2580 SelectObject(hdc, oldBrush);
\r
2581 SelectObject(tmphdc, oldBitmap);
\r
2585 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2586 int GetBackTextureMode( int algo )
\r
2588 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2592 case BACK_TEXTURE_MODE_PLAIN:
\r
2593 result = 1; /* Always use identity map */
\r
2595 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2596 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2604 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2605 to handle redraws cleanly (as random numbers would always be different).
\r
2607 VOID RebuildTextureSquareInfo()
\r
2617 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2619 if( liteBackTexture != NULL ) {
\r
2620 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2621 lite_w = bi.bmWidth;
\r
2622 lite_h = bi.bmHeight;
\r
2626 if( darkBackTexture != NULL ) {
\r
2627 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2628 dark_w = bi.bmWidth;
\r
2629 dark_h = bi.bmHeight;
\r
2633 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2634 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2635 if( (col + row) & 1 ) {
\r
2637 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2638 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2639 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2640 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2645 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2646 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2647 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2648 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2655 /* [AS] Arrow highlighting support */
\r
2657 static int A_WIDTH = 5; /* Width of arrow body */
\r
2659 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2660 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2662 static double Sqr( double x )
\r
2667 static int Round( double x )
\r
2669 return (int) (x + 0.5);
\r
2672 /* Draw an arrow between two points using current settings */
\r
2673 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2676 double dx, dy, j, k, x, y;
\r
2678 if( d_x == s_x ) {
\r
2679 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2681 arrow[0].x = s_x + A_WIDTH;
\r
2684 arrow[1].x = s_x + A_WIDTH;
\r
2685 arrow[1].y = d_y - h;
\r
2687 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2688 arrow[2].y = d_y - h;
\r
2693 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2694 arrow[4].y = d_y - h;
\r
2696 arrow[5].x = s_x - A_WIDTH;
\r
2697 arrow[5].y = d_y - h;
\r
2699 arrow[6].x = s_x - A_WIDTH;
\r
2702 else if( d_y == s_y ) {
\r
2703 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2706 arrow[0].y = s_y + A_WIDTH;
\r
2708 arrow[1].x = d_x - w;
\r
2709 arrow[1].y = s_y + A_WIDTH;
\r
2711 arrow[2].x = d_x - w;
\r
2712 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2717 arrow[4].x = d_x - w;
\r
2718 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2720 arrow[5].x = d_x - w;
\r
2721 arrow[5].y = s_y - A_WIDTH;
\r
2724 arrow[6].y = s_y - A_WIDTH;
\r
2727 /* [AS] Needed a lot of paper for this! :-) */
\r
2728 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2729 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2731 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2733 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2738 arrow[0].x = Round(x - j);
\r
2739 arrow[0].y = Round(y + j*dx);
\r
2741 arrow[1].x = Round(x + j);
\r
2742 arrow[1].y = Round(y - j*dx);
\r
2745 x = (double) d_x - k;
\r
2746 y = (double) d_y - k*dy;
\r
2749 x = (double) d_x + k;
\r
2750 y = (double) d_y + k*dy;
\r
2753 arrow[2].x = Round(x + j);
\r
2754 arrow[2].y = Round(y - j*dx);
\r
2756 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2757 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2762 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2763 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2765 arrow[6].x = Round(x - j);
\r
2766 arrow[6].y = Round(y + j*dx);
\r
2769 Polygon( hdc, arrow, 7 );
\r
2772 /* [AS] Draw an arrow between two squares */
\r
2773 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2775 int s_x, s_y, d_x, d_y;
\r
2782 if( s_col == d_col && s_row == d_row ) {
\r
2786 /* Get source and destination points */
\r
2787 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2788 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2791 d_y += squareSize / 4;
\r
2793 else if( d_y < s_y ) {
\r
2794 d_y += 3 * squareSize / 4;
\r
2797 d_y += squareSize / 2;
\r
2801 d_x += squareSize / 4;
\r
2803 else if( d_x < s_x ) {
\r
2804 d_x += 3 * squareSize / 4;
\r
2807 d_x += squareSize / 2;
\r
2810 s_x += squareSize / 2;
\r
2811 s_y += squareSize / 2;
\r
2813 /* Adjust width */
\r
2814 A_WIDTH = squareSize / 14;
\r
2817 stLB.lbStyle = BS_SOLID;
\r
2818 stLB.lbColor = appData.highlightArrowColor;
\r
2821 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2822 holdpen = SelectObject( hdc, hpen );
\r
2823 hbrush = CreateBrushIndirect( &stLB );
\r
2824 holdbrush = SelectObject( hdc, hbrush );
\r
2826 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2828 SelectObject( hdc, holdpen );
\r
2829 SelectObject( hdc, holdbrush );
\r
2830 DeleteObject( hpen );
\r
2831 DeleteObject( hbrush );
\r
2834 BOOL HasHighlightInfo()
\r
2836 BOOL result = FALSE;
\r
2838 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2839 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2847 BOOL IsDrawArrowEnabled()
\r
2849 BOOL result = FALSE;
\r
2851 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2858 VOID DrawArrowHighlight( HDC hdc )
\r
2860 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2861 DrawArrowBetweenSquares( hdc,
\r
2862 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2863 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2867 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2869 HRGN result = NULL;
\r
2871 if( HasHighlightInfo() ) {
\r
2872 int x1, y1, x2, y2;
\r
2873 int sx, sy, dx, dy;
\r
2875 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2876 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2878 sx = MIN( x1, x2 );
\r
2879 sy = MIN( y1, y2 );
\r
2880 dx = MAX( x1, x2 ) + squareSize;
\r
2881 dy = MAX( y1, y2 ) + squareSize;
\r
2883 result = CreateRectRgn( sx, sy, dx, dy );
\r
2890 Warning: this function modifies the behavior of several other functions.
\r
2892 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2893 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2894 repaint is scattered all over the place, which is not good for features such as
\r
2895 "arrow highlighting" that require a full repaint of the board.
\r
2897 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2898 user interaction, when speed is not so important) but especially to avoid errors
\r
2899 in the displayed graphics.
\r
2901 In such patched places, I always try refer to this function so there is a single
\r
2902 place to maintain knowledge.
\r
2904 To restore the original behavior, just return FALSE unconditionally.
\r
2906 BOOL IsFullRepaintPreferrable()
\r
2908 BOOL result = FALSE;
\r
2910 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2911 /* Arrow may appear on the board */
\r
2919 This function is called by DrawPosition to know whether a full repaint must
\r
2922 Only DrawPosition may directly call this function, which makes use of
\r
2923 some state information. Other function should call DrawPosition specifying
\r
2924 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2926 BOOL DrawPositionNeedsFullRepaint()
\r
2928 BOOL result = FALSE;
\r
2931 Probably a slightly better policy would be to trigger a full repaint
\r
2932 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2933 but animation is fast enough that it's difficult to notice.
\r
2935 if( animInfo.piece == EmptySquare ) {
\r
2936 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2945 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2947 int row, column, x, y, square_color, piece_color;
\r
2948 ChessSquare piece;
\r
2950 HDC texture_hdc = NULL;
\r
2952 /* [AS] Initialize background textures if needed */
\r
2953 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2954 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2955 if( backTextureSquareSize != squareSize
\r
2956 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2957 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2958 backTextureSquareSize = squareSize;
\r
2959 RebuildTextureSquareInfo();
\r
2962 texture_hdc = CreateCompatibleDC( hdc );
\r
2965 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2966 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2968 SquareToPos(row, column, &x, &y);
\r
2970 piece = board[row][column];
\r
2972 square_color = ((column + row) % 2) == 1;
\r
2973 if( gameInfo.variant == VariantXiangqi ) {
\r
2974 square_color = !InPalace(row, column);
\r
2975 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2976 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2978 piece_color = (int) piece < (int) BlackPawn;
\r
2981 /* [HGM] holdings file: light square or black */
\r
2982 if(column == BOARD_LEFT-2) {
\r
2983 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2986 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
2990 if(column == BOARD_RGHT + 1 ) {
\r
2991 if( row < gameInfo.holdingsSize )
\r
2994 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
2998 if(column == BOARD_LEFT-1 ) /* left align */
\r
2999 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3000 else if( column == BOARD_RGHT) /* right align */
\r
3001 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3003 if (appData.monoMode) {
\r
3004 if (piece == EmptySquare) {
\r
3005 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3006 square_color ? WHITENESS : BLACKNESS);
\r
3008 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3011 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3012 /* [AS] Draw the square using a texture bitmap */
\r
3013 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3014 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3015 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3018 squareSize, squareSize,
\r
3021 backTextureSquareInfo[r][c].mode,
\r
3022 backTextureSquareInfo[r][c].x,
\r
3023 backTextureSquareInfo[r][c].y );
\r
3025 SelectObject( texture_hdc, hbm );
\r
3027 if (piece != EmptySquare) {
\r
3028 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3032 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3034 oldBrush = SelectObject(hdc, brush );
\r
3035 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3036 SelectObject(hdc, oldBrush);
\r
3037 if (piece != EmptySquare)
\r
3038 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3043 if( texture_hdc != NULL ) {
\r
3044 DeleteDC( texture_hdc );
\r
3048 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3049 void fputDW(FILE *f, int x)
\r
3051 fputc(x & 255, f);
\r
3052 fputc(x>>8 & 255, f);
\r
3053 fputc(x>>16 & 255, f);
\r
3054 fputc(x>>24 & 255, f);
\r
3057 #define MAX_CLIPS 200 /* more than enough */
\r
3060 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3062 // HBITMAP bufferBitmap;
\r
3067 int w = 100, h = 50;
\r
3069 if(logo == NULL) return;
\r
3070 // GetClientRect(hwndMain, &Rect);
\r
3071 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3072 // Rect.bottom-Rect.top+1);
\r
3073 tmphdc = CreateCompatibleDC(hdc);
\r
3074 hbm = SelectObject(tmphdc, logo);
\r
3075 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3079 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3080 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3081 SelectObject(tmphdc, hbm);
\r
3086 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3088 static Board lastReq, lastDrawn;
\r
3089 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3090 static int lastDrawnFlipView = 0;
\r
3091 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3092 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3095 HBITMAP bufferBitmap;
\r
3096 HBITMAP oldBitmap;
\r
3098 HRGN clips[MAX_CLIPS];
\r
3099 ChessSquare dragged_piece = EmptySquare;
\r
3101 /* I'm undecided on this - this function figures out whether a full
\r
3102 * repaint is necessary on its own, so there's no real reason to have the
\r
3103 * caller tell it that. I think this can safely be set to FALSE - but
\r
3104 * if we trust the callers not to request full repaints unnessesarily, then
\r
3105 * we could skip some clipping work. In other words, only request a full
\r
3106 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3107 * gamestart and similar) --Hawk
\r
3109 Boolean fullrepaint = repaint;
\r
3111 if( DrawPositionNeedsFullRepaint() ) {
\r
3112 fullrepaint = TRUE;
\r
3115 if (board == NULL) {
\r
3116 if (!lastReqValid) {
\r
3121 CopyBoard(lastReq, board);
\r
3125 if (doingSizing) {
\r
3129 if (IsIconic(hwndMain)) {
\r
3133 if (hdc == NULL) {
\r
3134 hdc = GetDC(hwndMain);
\r
3135 if (!appData.monoMode) {
\r
3136 SelectPalette(hdc, hPal, FALSE);
\r
3137 RealizePalette(hdc);
\r
3141 releaseDC = FALSE;
\r
3144 /* Create some work-DCs */
\r
3145 hdcmem = CreateCompatibleDC(hdc);
\r
3146 tmphdc = CreateCompatibleDC(hdc);
\r
3148 /* If dragging is in progress, we temporarely remove the piece */
\r
3149 /* [HGM] or temporarily decrease count if stacked */
\r
3150 /* !! Moved to before board compare !! */
\r
3151 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3152 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3153 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3154 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3155 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3157 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3158 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3159 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3161 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3164 /* Figure out which squares need updating by comparing the
\r
3165 * newest board with the last drawn board and checking if
\r
3166 * flipping has changed.
\r
3168 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3169 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3170 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3171 if (lastDrawn[row][column] != board[row][column]) {
\r
3172 SquareToPos(row, column, &x, &y);
\r
3173 clips[num_clips++] =
\r
3174 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3178 for (i=0; i<2; i++) {
\r
3179 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3180 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3181 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3182 lastDrawnHighlight.sq[i].y >= 0) {
\r
3183 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3184 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3185 clips[num_clips++] =
\r
3186 CreateRectRgn(x - lineGap, y - lineGap,
\r
3187 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3189 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3190 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3191 clips[num_clips++] =
\r
3192 CreateRectRgn(x - lineGap, y - lineGap,
\r
3193 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3197 for (i=0; i<2; i++) {
\r
3198 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3199 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3200 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3201 lastDrawnPremove.sq[i].y >= 0) {
\r
3202 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3203 lastDrawnPremove.sq[i].x, &x, &y);
\r
3204 clips[num_clips++] =
\r
3205 CreateRectRgn(x - lineGap, y - lineGap,
\r
3206 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3208 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3209 premoveHighlightInfo.sq[i].y >= 0) {
\r
3210 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3211 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3212 clips[num_clips++] =
\r
3213 CreateRectRgn(x - lineGap, y - lineGap,
\r
3214 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3219 fullrepaint = TRUE;
\r
3222 /* Create a buffer bitmap - this is the actual bitmap
\r
3223 * being written to. When all the work is done, we can
\r
3224 * copy it to the real DC (the screen). This avoids
\r
3225 * the problems with flickering.
\r
3227 GetClientRect(hwndMain, &Rect);
\r
3228 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3229 Rect.bottom-Rect.top+1);
\r
3230 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3231 if (!appData.monoMode) {
\r
3232 SelectPalette(hdcmem, hPal, FALSE);
\r
3235 /* Create clips for dragging */
\r
3236 if (!fullrepaint) {
\r
3237 if (dragInfo.from.x >= 0) {
\r
3238 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3239 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3241 if (dragInfo.start.x >= 0) {
\r
3242 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3243 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3245 if (dragInfo.pos.x >= 0) {
\r
3246 x = dragInfo.pos.x - squareSize / 2;
\r
3247 y = dragInfo.pos.y - squareSize / 2;
\r
3248 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3250 if (dragInfo.lastpos.x >= 0) {
\r
3251 x = dragInfo.lastpos.x - squareSize / 2;
\r
3252 y = dragInfo.lastpos.y - squareSize / 2;
\r
3253 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3257 /* Are we animating a move?
\r
3259 * - remove the piece from the board (temporarely)
\r
3260 * - calculate the clipping region
\r
3262 if (!fullrepaint) {
\r
3263 if (animInfo.piece != EmptySquare) {
\r
3264 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3265 x = boardRect.left + animInfo.lastpos.x;
\r
3266 y = boardRect.top + animInfo.lastpos.y;
\r
3267 x2 = boardRect.left + animInfo.pos.x;
\r
3268 y2 = boardRect.top + animInfo.pos.y;
\r
3269 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3270 /* Slight kludge. The real problem is that after AnimateMove is
\r
3271 done, the position on the screen does not match lastDrawn.
\r
3272 This currently causes trouble only on e.p. captures in
\r
3273 atomic, where the piece moves to an empty square and then
\r
3274 explodes. The old and new positions both had an empty square
\r
3275 at the destination, but animation has drawn a piece there and
\r
3276 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3277 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3281 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3282 if (num_clips == 0)
\r
3283 fullrepaint = TRUE;
\r
3285 /* Set clipping on the memory DC */
\r
3286 if (!fullrepaint) {
\r
3287 SelectClipRgn(hdcmem, clips[0]);
\r
3288 for (x = 1; x < num_clips; x++) {
\r
3289 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3290 abort(); // this should never ever happen!
\r
3294 /* Do all the drawing to the memory DC */
\r
3295 if(explodeInfo.radius) { // [HGM] atomic
\r
3297 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3298 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3299 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3300 x += squareSize/2;
\r
3301 y += squareSize/2;
\r
3302 if(!fullrepaint) {
\r
3303 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3304 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3306 DrawGridOnDC(hdcmem);
\r
3307 DrawHighlightsOnDC(hdcmem);
\r
3308 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3309 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3310 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3311 SelectObject(hdcmem, oldBrush);
\r
3313 DrawGridOnDC(hdcmem);
\r
3314 DrawHighlightsOnDC(hdcmem);
\r
3315 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3317 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3318 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3319 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3320 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3321 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3322 SquareToPos(row, column, &x, &y);
\r
3323 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3324 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3325 SelectObject(hdcmem, oldBrush);
\r
3330 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3331 if(appData.autoLogo) {
\r
3333 switch(gameMode) { // pick logos based on game mode
\r
3334 case IcsObserving:
\r
3335 whiteLogo = second.programLogo; // ICS logo
\r
3336 blackLogo = second.programLogo;
\r
3339 case IcsPlayingWhite:
\r
3340 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3341 blackLogo = second.programLogo; // ICS logo
\r
3343 case IcsPlayingBlack:
\r
3344 whiteLogo = second.programLogo; // ICS logo
\r
3345 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3347 case TwoMachinesPlay:
\r
3348 if(first.twoMachinesColor[0] == 'b') {
\r
3349 whiteLogo = second.programLogo;
\r
3350 blackLogo = first.programLogo;
\r
3353 case MachinePlaysWhite:
\r
3354 blackLogo = userLogo;
\r
3356 case MachinePlaysBlack:
\r
3357 whiteLogo = userLogo;
\r
3358 blackLogo = first.programLogo;
\r
3361 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3362 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3365 if( appData.highlightMoveWithArrow ) {
\r
3366 DrawArrowHighlight(hdcmem);
\r
3369 DrawCoordsOnDC(hdcmem);
\r
3371 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3372 /* to make sure lastDrawn contains what is actually drawn */
\r
3374 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3375 if (dragged_piece != EmptySquare) {
\r
3376 /* [HGM] or restack */
\r
3377 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3378 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3380 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3381 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3382 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3383 x = dragInfo.pos.x - squareSize / 2;
\r
3384 y = dragInfo.pos.y - squareSize / 2;
\r
3385 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3386 ((int) dragged_piece < (int) BlackPawn),
\r
3387 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3390 /* Put the animated piece back into place and draw it */
\r
3391 if (animInfo.piece != EmptySquare) {
\r
3392 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3393 x = boardRect.left + animInfo.pos.x;
\r
3394 y = boardRect.top + animInfo.pos.y;
\r
3395 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3396 ((int) animInfo.piece < (int) BlackPawn),
\r
3397 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3400 /* Release the bufferBitmap by selecting in the old bitmap
\r
3401 * and delete the memory DC
\r
3403 SelectObject(hdcmem, oldBitmap);
\r
3406 /* Set clipping on the target DC */
\r
3407 if (!fullrepaint) {
\r
3408 SelectClipRgn(hdc, clips[0]);
\r
3409 for (x = 1; x < num_clips; x++) {
\r
3410 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3411 abort(); // this should never ever happen!
\r
3415 /* Copy the new bitmap onto the screen in one go.
\r
3416 * This way we avoid any flickering
\r
3418 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3419 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3420 boardRect.right - boardRect.left,
\r
3421 boardRect.bottom - boardRect.top,
\r
3422 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3423 if(saveDiagFlag) {
\r
3424 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3425 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3427 GetObject(bufferBitmap, sizeof(b), &b);
\r
3428 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3429 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3430 bih.biWidth = b.bmWidth;
\r
3431 bih.biHeight = b.bmHeight;
\r
3433 bih.biBitCount = b.bmBitsPixel;
\r
3434 bih.biCompression = 0;
\r
3435 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3436 bih.biXPelsPerMeter = 0;
\r
3437 bih.biYPelsPerMeter = 0;
\r
3438 bih.biClrUsed = 0;
\r
3439 bih.biClrImportant = 0;
\r
3440 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3441 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3442 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3443 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3445 wb = b.bmWidthBytes;
\r
3447 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3448 int k = ((int*) pData)[i];
\r
3449 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3450 if(j >= 16) break;
\r
3452 if(j >= nrColors) nrColors = j+1;
\r
3454 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3456 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3457 for(w=0; w<(wb>>2); w+=2) {
\r
3458 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3459 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3460 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3461 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3462 pData[p++] = m | j<<4;
\r
3464 while(p&3) pData[p++] = 0;
\r
3467 wb = ((wb+31)>>5)<<2;
\r
3469 // write BITMAPFILEHEADER
\r
3470 fprintf(diagFile, "BM");
\r
3471 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3472 fputDW(diagFile, 0);
\r
3473 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3474 // write BITMAPINFOHEADER
\r
3475 fputDW(diagFile, 40);
\r
3476 fputDW(diagFile, b.bmWidth);
\r
3477 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3478 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3479 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3480 fputDW(diagFile, 0);
\r
3481 fputDW(diagFile, 0);
\r
3482 fputDW(diagFile, 0);
\r
3483 fputDW(diagFile, 0);
\r
3484 fputDW(diagFile, 0);
\r
3485 fputDW(diagFile, 0);
\r
3486 // write color table
\r
3488 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3489 // write bitmap data
\r
3490 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3491 fputc(pData[i], diagFile);
\r
3495 SelectObject(tmphdc, oldBitmap);
\r
3497 /* Massive cleanup */
\r
3498 for (x = 0; x < num_clips; x++)
\r
3499 DeleteObject(clips[x]);
\r
3502 DeleteObject(bufferBitmap);
\r
3505 ReleaseDC(hwndMain, hdc);
\r
3507 if (lastDrawnFlipView != flipView) {
\r
3509 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3511 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3514 /* CopyBoard(lastDrawn, board);*/
\r
3515 lastDrawnHighlight = highlightInfo;
\r
3516 lastDrawnPremove = premoveHighlightInfo;
\r
3517 lastDrawnFlipView = flipView;
\r
3518 lastDrawnValid = 1;
\r
3521 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3526 saveDiagFlag = 1; diagFile = f;
\r
3527 HDCDrawPosition(NULL, TRUE, NULL);
\r
3531 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3538 /*---------------------------------------------------------------------------*\
\r
3539 | CLIENT PAINT PROCEDURE
\r
3540 | This is the main event-handler for the WM_PAINT message.
\r
3542 \*---------------------------------------------------------------------------*/
\r
3544 PaintProc(HWND hwnd)
\r
3550 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3551 if (IsIconic(hwnd)) {
\r
3552 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3554 if (!appData.monoMode) {
\r
3555 SelectPalette(hdc, hPal, FALSE);
\r
3556 RealizePalette(hdc);
\r
3558 HDCDrawPosition(hdc, 1, NULL);
\r
3560 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3561 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3562 ETO_CLIPPED|ETO_OPAQUE,
\r
3563 &messageRect, messageText, strlen(messageText), NULL);
\r
3564 SelectObject(hdc, oldFont);
\r
3565 DisplayBothClocks();
\r
3567 EndPaint(hwnd,&ps);
\r
3575 * If the user selects on a border boundary, return -1; if off the board,
\r
3576 * return -2. Otherwise map the event coordinate to the square.
\r
3577 * The offset boardRect.left or boardRect.top must already have been
\r
3578 * subtracted from x.
\r
3580 int EventToSquare(x, limit)
\r
3588 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3590 x /= (squareSize + lineGap);
\r
3602 DropEnable dropEnables[] = {
\r
3603 { 'P', DP_Pawn, "Pawn" },
\r
3604 { 'N', DP_Knight, "Knight" },
\r
3605 { 'B', DP_Bishop, "Bishop" },
\r
3606 { 'R', DP_Rook, "Rook" },
\r
3607 { 'Q', DP_Queen, "Queen" },
\r
3611 SetupDropMenu(HMENU hmenu)
\r
3613 int i, count, enable;
\r
3615 extern char white_holding[], black_holding[];
\r
3616 char item[MSG_SIZ];
\r
3618 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3619 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3620 dropEnables[i].piece);
\r
3622 while (p && *p++ == dropEnables[i].piece) count++;
\r
3623 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3624 enable = count > 0 || !appData.testLegality
\r
3625 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3626 && !appData.icsActive);
\r
3627 ModifyMenu(hmenu, dropEnables[i].command,
\r
3628 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3629 dropEnables[i].command, item);
\r
3633 void DragPieceBegin(int x, int y)
\r
3635 dragInfo.lastpos.x = boardRect.left + x;
\r
3636 dragInfo.lastpos.y = boardRect.top + y;
\r
3637 dragInfo.from.x = fromX;
\r
3638 dragInfo.from.y = fromY;
\r
3639 dragInfo.start = dragInfo.from;
\r
3640 SetCapture(hwndMain);
\r
3643 void DragPieceEnd(int x, int y)
\r
3646 dragInfo.start.x = dragInfo.start.y = -1;
\r
3647 dragInfo.from = dragInfo.start;
\r
3648 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3651 /* Event handler for mouse messages */
\r
3653 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3657 static int recursive = 0;
\r
3659 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3662 if (message == WM_MBUTTONUP) {
\r
3663 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3664 to the middle button: we simulate pressing the left button too!
\r
3666 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3667 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3673 pt.x = LOWORD(lParam);
\r
3674 pt.y = HIWORD(lParam);
\r
3675 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3676 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3677 if (!flipView && y >= 0) {
\r
3678 y = BOARD_HEIGHT - 1 - y;
\r
3680 if (flipView && x >= 0) {
\r
3681 x = BOARD_WIDTH - 1 - x;
\r
3684 switch (message) {
\r
3685 case WM_LBUTTONDOWN:
\r
3686 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3687 if (gameMode == EditPosition) {
\r
3688 SetWhiteToPlayEvent();
\r
3689 } else if (gameMode == IcsPlayingBlack ||
\r
3690 gameMode == MachinePlaysWhite) {
\r
3692 } else if (gameMode == EditGame) {
\r
3693 AdjustClock(flipClock, -1);
\r
3695 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3696 if (gameMode == EditPosition) {
\r
3697 SetBlackToPlayEvent();
\r
3698 } else if (gameMode == IcsPlayingWhite ||
\r
3699 gameMode == MachinePlaysBlack) {
\r
3701 } else if (gameMode == EditGame) {
\r
3702 AdjustClock(!flipClock, -1);
\r
3705 dragInfo.start.x = dragInfo.start.y = -1;
\r
3706 dragInfo.from = dragInfo.start;
\r
3707 if(fromX == -1 && frozen) { // not sure where this is for
\r
3708 fromX = fromY = -1;
\r
3709 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3712 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3713 DrawPosition(TRUE, NULL);
\r
3716 case WM_LBUTTONUP:
\r
3717 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3718 DrawPosition(TRUE, NULL);
\r
3721 case WM_MOUSEMOVE:
\r
3722 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
3723 if ((appData.animateDragging || appData.highlightDragging)
\r
3724 && (wParam & MK_LBUTTON)
\r
3725 && dragInfo.from.x >= 0)
\r
3727 BOOL full_repaint = FALSE;
\r
3729 if (appData.animateDragging) {
\r
3730 dragInfo.pos = pt;
\r
3732 if (appData.highlightDragging) {
\r
3733 SetHighlights(fromX, fromY, x, y);
\r
3734 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3735 full_repaint = TRUE;
\r
3739 DrawPosition( full_repaint, NULL);
\r
3741 dragInfo.lastpos = dragInfo.pos;
\r
3745 case WM_MOUSEWHEEL: // [DM]
\r
3746 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3747 /* Mouse Wheel is being rolled forward
\r
3748 * Play moves forward
\r
3750 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3751 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3752 /* Mouse Wheel is being rolled backward
\r
3753 * Play moves backward
\r
3755 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3756 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3766 case WM_MBUTTONDOWN:
\r
3767 case WM_RBUTTONDOWN:
\r
3770 fromX = fromY = -1;
\r
3771 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3772 dragInfo.start.x = dragInfo.start.y = -1;
\r
3773 dragInfo.from = dragInfo.start;
\r
3774 dragInfo.lastpos = dragInfo.pos;
\r
3775 if (appData.highlightDragging) {
\r
3776 ClearHighlights();
\r
3779 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3780 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3781 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3782 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3783 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3786 DrawPosition(TRUE, NULL);
\r
3788 switch (gameMode) {
\r
3789 case IcsExamining:
\r
3790 if(x < BOARD_LEFT || x >= BOARD_RGHT) break;
\r
3791 case EditPosition:
\r
3792 if (x == BOARD_LEFT-1 || x == BOARD_RGHT) break;
\r
3793 if (x < 0 || y < 0) break;
\r
3796 if (message == WM_MBUTTONDOWN) {
\r
3797 buttonCount = 3; /* even if system didn't think so */
\r
3798 if (wParam & MK_SHIFT)
\r
3799 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3801 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3802 } else { /* message == WM_RBUTTONDOWN */
\r
3803 /* Just have one menu, on the right button. Windows users don't
\r
3804 think to try the middle one, and sometimes other software steals
\r
3805 it, or it doesn't really exist. */
\r
3806 if(gameInfo.variant != VariantShogi)
\r
3807 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3809 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3813 if(!appData.icsEngineAnalyze) break;
3814 case IcsPlayingWhite:
3815 case IcsPlayingBlack:
3816 if(!appData.zippyPlay) goto noZip;
3817 case MachinePlaysWhite:
3818 case MachinePlaysBlack:
3819 case TwoMachinesPlay:
3822 if (!appData.dropMenu) {
3823 SetCapture(hwndMain);
3824 LoadPV(pt.x - boardRect.left, pt.y - boardRect.top);
3827 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
3828 gameMode == AnalyzeFile || gameMode == IcsObserving) break;
3831 if (x < 0 || y < 0) break;
3832 if (!appData.dropMenu || appData.testLegality &&
3833 gameInfo.variant != VariantBughouse &&
3834 gameInfo.variant != VariantCrazyhouse) break;
3837 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3838 SetupDropMenu(hmenu);
\r
3839 MenuPopup(hwnd, pt, hmenu, -1);
\r
3850 /* Preprocess messages for buttons in main window */
\r
3852 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3854 int id = GetWindowLong(hwnd, GWL_ID);
\r
3857 for (i=0; i<N_BUTTONS; i++) {
\r
3858 if (buttonDesc[i].id == id) break;
\r
3860 if (i == N_BUTTONS) return 0;
\r
3861 switch (message) {
\r
3866 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3867 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3874 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3877 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3878 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3879 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3880 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3882 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3884 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3885 PopUpMoveDialog((char)wParam);
\r
3891 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3894 /* Process messages for Promotion dialog box */
\r
3896 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3900 switch (message) {
\r
3901 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3902 /* Center the dialog over the application window */
\r
3903 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3904 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3905 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3906 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3907 SW_SHOW : SW_HIDE);
\r
3908 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3909 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3910 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3911 PieceToChar(WhiteAngel) != '~') ||
\r
3912 (PieceToChar(BlackAngel) >= 'A' &&
\r
3913 PieceToChar(BlackAngel) != '~') ) ?
\r
3914 SW_SHOW : SW_HIDE);
\r
3915 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3916 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3917 PieceToChar(WhiteMarshall) != '~') ||
\r
3918 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3919 PieceToChar(BlackMarshall) != '~') ) ?
\r
3920 SW_SHOW : SW_HIDE);
\r
3921 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3922 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3923 gameInfo.variant != VariantShogi ?
\r
3924 SW_SHOW : SW_HIDE);
\r
3925 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3926 gameInfo.variant != VariantShogi ?
\r
3927 SW_SHOW : SW_HIDE);
\r
3928 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3929 gameInfo.variant == VariantShogi ?
\r
3930 SW_SHOW : SW_HIDE);
\r
3931 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3932 gameInfo.variant == VariantShogi ?
\r
3933 SW_SHOW : SW_HIDE);
\r
3934 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3935 gameInfo.variant == VariantSuper ?
\r
3936 SW_SHOW : SW_HIDE);
\r
3939 case WM_COMMAND: /* message: received a command */
\r
3940 switch (LOWORD(wParam)) {
\r
3942 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3943 ClearHighlights();
\r
3944 DrawPosition(FALSE, NULL);
\r
3947 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3950 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3953 promoChar = PieceToChar(BlackRook);
\r
3956 promoChar = PieceToChar(BlackBishop);
\r
3958 case PB_Chancellor:
\r
3959 promoChar = PieceToChar(BlackMarshall);
\r
3961 case PB_Archbishop:
\r
3962 promoChar = PieceToChar(BlackAngel);
\r
3965 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
3970 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3971 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
3972 only show the popup when we are already sure the move is valid or
\r
3973 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
3974 will figure out it is a promotion from the promoChar. */
\r
3975 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
3976 fromX = fromY = -1;
\r
3977 if (!appData.highlightLastMove) {
\r
3978 ClearHighlights();
\r
3979 DrawPosition(FALSE, NULL);
\r
3986 /* Pop up promotion dialog */
\r
3988 PromotionPopup(HWND hwnd)
\r
3992 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
3993 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
3994 hwnd, (DLGPROC)lpProc);
\r
3995 FreeProcInstance(lpProc);
\r
4001 DrawPosition(TRUE, NULL);
\r
4002 PromotionPopup(hwndMain);
\r
4005 /* Toggle ShowThinking */
\r
4007 ToggleShowThinking()
\r
4009 appData.showThinking = !appData.showThinking;
\r
4010 ShowThinkingEvent();
\r
4014 LoadGameDialog(HWND hwnd, char* title)
\r
4018 char fileTitle[MSG_SIZ];
\r
4019 f = OpenFileDialog(hwnd, "rb", "",
\r
4020 appData.oldSaveStyle ? "gam" : "pgn",
\r
4022 title, &number, fileTitle, NULL);
\r
4024 cmailMsgLoaded = FALSE;
\r
4025 if (number == 0) {
\r
4026 int error = GameListBuild(f);
\r
4028 DisplayError("Cannot build game list", error);
\r
4029 } else if (!ListEmpty(&gameList) &&
\r
4030 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4031 GameListPopUp(f, fileTitle);
\r
4034 GameListDestroy();
\r
4037 LoadGame(f, number, fileTitle, FALSE);
\r
4041 int get_term_width()
\r
4046 HFONT hfont, hold_font;
\r
4051 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4055 // get the text metrics
\r
4056 hdc = GetDC(hText);
\r
4057 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4058 if (consoleCF.dwEffects & CFE_BOLD)
\r
4059 lf.lfWeight = FW_BOLD;
\r
4060 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4061 lf.lfItalic = TRUE;
\r
4062 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4063 lf.lfStrikeOut = TRUE;
\r
4064 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4065 lf.lfUnderline = TRUE;
\r
4066 hfont = CreateFontIndirect(&lf);
\r
4067 hold_font = SelectObject(hdc, hfont);
\r
4068 GetTextMetrics(hdc, &tm);
\r
4069 SelectObject(hdc, hold_font);
\r
4070 DeleteObject(hfont);
\r
4071 ReleaseDC(hText, hdc);
\r
4073 // get the rectangle
\r
4074 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4076 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4079 void UpdateICSWidth(HWND hText)
\r
4081 LONG old_width, new_width;
\r
4083 new_width = get_term_width(hText, FALSE);
\r
4084 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4085 if (new_width != old_width)
\r
4087 ics_update_width(new_width);
\r
4088 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4093 ChangedConsoleFont()
\r
4096 CHARRANGE tmpsel, sel;
\r
4097 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4098 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4099 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4102 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4103 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4104 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4105 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4106 * size. This was undocumented in the version of MSVC++ that I had
\r
4107 * when I wrote the code, but is apparently documented now.
\r
4109 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4110 cfmt.bCharSet = f->lf.lfCharSet;
\r
4111 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4112 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4113 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4114 /* Why are the following seemingly needed too? */
\r
4115 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4116 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4117 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4119 tmpsel.cpMax = -1; /*999999?*/
\r
4120 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4121 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4122 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4123 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4125 paraf.cbSize = sizeof(paraf);
\r
4126 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4127 paraf.dxStartIndent = 0;
\r
4128 paraf.dxOffset = WRAP_INDENT;
\r
4129 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4130 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4131 UpdateICSWidth(hText);
\r
4134 /*---------------------------------------------------------------------------*\
\r
4136 * Window Proc for main window
\r
4138 \*---------------------------------------------------------------------------*/
\r
4140 /* Process messages for main window, etc. */
\r
4142 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4145 int wmId, wmEvent;
\r
4149 char fileTitle[MSG_SIZ];
\r
4150 char buf[MSG_SIZ];
\r
4151 static SnapData sd;
\r
4153 switch (message) {
\r
4155 case WM_PAINT: /* message: repaint portion of window */
\r
4159 case WM_ERASEBKGND:
\r
4160 if (IsIconic(hwnd)) {
\r
4161 /* Cheat; change the message */
\r
4162 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4164 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4168 case WM_LBUTTONDOWN:
\r
4169 case WM_MBUTTONDOWN:
\r
4170 case WM_RBUTTONDOWN:
\r
4171 case WM_LBUTTONUP:
\r
4172 case WM_MBUTTONUP:
\r
4173 case WM_RBUTTONUP:
\r
4174 case WM_MOUSEMOVE:
\r
4175 case WM_MOUSEWHEEL:
\r
4176 MouseEvent(hwnd, message, wParam, lParam);
\r
4179 JAWS_KB_NAVIGATION
\r
4183 JAWS_ALT_INTERCEPT
\r
4185 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4186 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4187 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4188 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4190 SendMessage(h, message, wParam, lParam);
\r
4191 } else if(lParam != KF_REPEAT) {
\r
4192 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4193 PopUpMoveDialog((char)wParam);
\r
4194 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4195 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4200 case WM_PALETTECHANGED:
\r
4201 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4203 HDC hdc = GetDC(hwndMain);
\r
4204 SelectPalette(hdc, hPal, TRUE);
\r
4205 nnew = RealizePalette(hdc);
\r
4207 paletteChanged = TRUE;
\r
4208 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4210 ReleaseDC(hwnd, hdc);
\r
4214 case WM_QUERYNEWPALETTE:
\r
4215 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4217 HDC hdc = GetDC(hwndMain);
\r
4218 paletteChanged = FALSE;
\r
4219 SelectPalette(hdc, hPal, FALSE);
\r
4220 nnew = RealizePalette(hdc);
\r
4222 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4224 ReleaseDC(hwnd, hdc);
\r
4229 case WM_COMMAND: /* message: command from application menu */
\r
4230 wmId = LOWORD(wParam);
\r
4231 wmEvent = HIWORD(wParam);
\r
4236 SAY("new game enter a move to play against the computer with white");
\r
4239 case IDM_NewGameFRC:
\r
4240 if( NewGameFRC() == 0 ) {
\r
4245 case IDM_NewVariant:
\r
4246 NewVariantPopup(hwnd);
\r
4249 case IDM_LoadGame:
\r
4250 LoadGameDialog(hwnd, "Load Game from File");
\r
4253 case IDM_LoadNextGame:
\r
4257 case IDM_LoadPrevGame:
\r
4261 case IDM_ReloadGame:
\r
4265 case IDM_LoadPosition:
\r
4266 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4267 Reset(FALSE, TRUE);
\r
4270 f = OpenFileDialog(hwnd, "rb", "",
\r
4271 appData.oldSaveStyle ? "pos" : "fen",
\r
4273 "Load Position from File", &number, fileTitle, NULL);
\r
4275 LoadPosition(f, number, fileTitle);
\r
4279 case IDM_LoadNextPosition:
\r
4280 ReloadPosition(1);
\r
4283 case IDM_LoadPrevPosition:
\r
4284 ReloadPosition(-1);
\r
4287 case IDM_ReloadPosition:
\r
4288 ReloadPosition(0);
\r
4291 case IDM_SaveGame:
\r
4292 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4293 f = OpenFileDialog(hwnd, "a", defName,
\r
4294 appData.oldSaveStyle ? "gam" : "pgn",
\r
4296 "Save Game to File", NULL, fileTitle, NULL);
\r
4298 SaveGame(f, 0, "");
\r
4302 case IDM_SavePosition:
\r
4303 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4304 f = OpenFileDialog(hwnd, "a", defName,
\r
4305 appData.oldSaveStyle ? "pos" : "fen",
\r
4307 "Save Position to File", NULL, fileTitle, NULL);
\r
4309 SavePosition(f, 0, "");
\r
4313 case IDM_SaveDiagram:
\r
4314 defName = "diagram";
\r
4315 f = OpenFileDialog(hwnd, "wb", defName,
\r
4318 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4324 case IDM_CopyGame:
\r
4325 CopyGameToClipboard();
\r
4328 case IDM_PasteGame:
\r
4329 PasteGameFromClipboard();
\r
4332 case IDM_CopyGameListToClipboard:
\r
4333 CopyGameListToClipboard();
\r
4336 /* [AS] Autodetect FEN or PGN data */
\r
4337 case IDM_PasteAny:
\r
4338 PasteGameOrFENFromClipboard();
\r
4341 /* [AS] Move history */
\r
4342 case IDM_ShowMoveHistory:
\r
4343 if( MoveHistoryIsUp() ) {
\r
4344 MoveHistoryPopDown();
\r
4347 MoveHistoryPopUp();
\r
4351 /* [AS] Eval graph */
\r
4352 case IDM_ShowEvalGraph:
\r
4353 if( EvalGraphIsUp() ) {
\r
4354 EvalGraphPopDown();
\r
4358 SetFocus(hwndMain);
\r
4362 /* [AS] Engine output */
\r
4363 case IDM_ShowEngineOutput:
\r
4364 if( EngineOutputIsUp() ) {
\r
4365 EngineOutputPopDown();
\r
4368 EngineOutputPopUp();
\r
4372 /* [AS] User adjudication */
\r
4373 case IDM_UserAdjudication_White:
\r
4374 UserAdjudicationEvent( +1 );
\r
4377 case IDM_UserAdjudication_Black:
\r
4378 UserAdjudicationEvent( -1 );
\r
4381 case IDM_UserAdjudication_Draw:
\r
4382 UserAdjudicationEvent( 0 );
\r
4385 /* [AS] Game list options dialog */
\r
4386 case IDM_GameListOptions:
\r
4387 GameListOptions();
\r
4394 case IDM_CopyPosition:
\r
4395 CopyFENToClipboard();
\r
4398 case IDM_PastePosition:
\r
4399 PasteFENFromClipboard();
\r
4402 case IDM_MailMove:
\r
4406 case IDM_ReloadCMailMsg:
\r
4407 Reset(TRUE, TRUE);
\r
4408 ReloadCmailMsgEvent(FALSE);
\r
4411 case IDM_Minimize:
\r
4412 ShowWindow(hwnd, SW_MINIMIZE);
\r
4419 case IDM_MachineWhite:
\r
4420 MachineWhiteEvent();
\r
4422 * refresh the tags dialog only if it's visible
\r
4424 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4426 tags = PGNTags(&gameInfo);
\r
4427 TagsPopUp(tags, CmailMsg());
\r
4430 SAY("computer starts playing white");
\r
4433 case IDM_MachineBlack:
\r
4434 MachineBlackEvent();
\r
4436 * refresh the tags dialog only if it's visible
\r
4438 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4440 tags = PGNTags(&gameInfo);
\r
4441 TagsPopUp(tags, CmailMsg());
\r
4444 SAY("computer starts playing black");
\r
4447 case IDM_TwoMachines:
\r
4448 TwoMachinesEvent();
\r
4450 * refresh the tags dialog only if it's visible
\r
4452 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4454 tags = PGNTags(&gameInfo);
\r
4455 TagsPopUp(tags, CmailMsg());
\r
4458 SAY("programs start playing each other");
\r
4461 case IDM_AnalysisMode:
\r
4462 if (!first.analysisSupport) {
\r
4463 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4464 DisplayError(buf, 0);
\r
4466 SAY("analyzing current position");
\r
4467 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4468 if (appData.icsActive) {
\r
4469 if (gameMode != IcsObserving) {
\r
4470 sprintf(buf, "You are not observing a game");
\r
4471 DisplayError(buf, 0);
\r
4472 /* secure check */
\r
4473 if (appData.icsEngineAnalyze) {
\r
4474 if (appData.debugMode)
\r
4475 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4476 ExitAnalyzeMode();
\r
4482 /* if enable, user want disable icsEngineAnalyze */
\r
4483 if (appData.icsEngineAnalyze) {
\r
4484 ExitAnalyzeMode();
\r
4488 appData.icsEngineAnalyze = TRUE;
\r
4489 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4492 if (!appData.showThinking) ToggleShowThinking();
\r
4493 AnalyzeModeEvent();
\r
4497 case IDM_AnalyzeFile:
\r
4498 if (!first.analysisSupport) {
\r
4499 char buf[MSG_SIZ];
\r
4500 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4501 DisplayError(buf, 0);
\r
4503 if (!appData.showThinking) ToggleShowThinking();
\r
4504 AnalyzeFileEvent();
\r
4505 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4506 AnalysisPeriodicEvent(1);
\r
4510 case IDM_IcsClient:
\r
4514 case IDM_EditGame:
\r
4519 case IDM_EditPosition:
\r
4520 EditPositionEvent();
\r
4521 SAY("to set up a position type a FEN");
\r
4524 case IDM_Training:
\r
4528 case IDM_ShowGameList:
\r
4529 ShowGameListProc();
\r
4532 case IDM_EditTags:
\r
4536 case IDM_EditComment:
\r
4537 if (commentUp && editComment) {
\r
4540 EditCommentEvent();
\r
4560 case IDM_CallFlag:
\r
4580 case IDM_StopObserving:
\r
4581 StopObservingEvent();
\r
4584 case IDM_StopExamining:
\r
4585 StopExaminingEvent();
\r
4588 case IDM_TypeInMove:
\r
4589 PopUpMoveDialog('\000');
\r
4592 case IDM_TypeInName:
\r
4593 PopUpNameDialog('\000');
\r
4596 case IDM_Backward:
\r
4598 SetFocus(hwndMain);
\r
4605 SetFocus(hwndMain);
\r
4610 SetFocus(hwndMain);
\r
4615 SetFocus(hwndMain);
\r
4622 case IDM_TruncateGame:
\r
4623 TruncateGameEvent();
\r
4630 case IDM_RetractMove:
\r
4631 RetractMoveEvent();
\r
4634 case IDM_FlipView:
\r
4635 flipView = !flipView;
\r
4636 DrawPosition(FALSE, NULL);
\r
4639 case IDM_FlipClock:
\r
4640 flipClock = !flipClock;
\r
4641 DisplayBothClocks();
\r
4642 DrawPosition(FALSE, NULL);
\r
4645 case IDM_MuteSounds:
\r
4646 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4647 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4648 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4651 case IDM_GeneralOptions:
\r
4652 GeneralOptionsPopup(hwnd);
\r
4653 DrawPosition(TRUE, NULL);
\r
4656 case IDM_BoardOptions:
\r
4657 BoardOptionsPopup(hwnd);
\r
4660 case IDM_EnginePlayOptions:
\r
4661 EnginePlayOptionsPopup(hwnd);
\r
4664 case IDM_Engine1Options:
\r
4665 EngineOptionsPopup(hwnd, &first);
\r
4668 case IDM_Engine2Options:
\r
4669 EngineOptionsPopup(hwnd, &second);
\r
4672 case IDM_OptionsUCI:
\r
4673 UciOptionsPopup(hwnd);
\r
4676 case IDM_IcsOptions:
\r
4677 IcsOptionsPopup(hwnd);
\r
4681 FontsOptionsPopup(hwnd);
\r
4685 SoundOptionsPopup(hwnd);
\r
4688 case IDM_CommPort:
\r
4689 CommPortOptionsPopup(hwnd);
\r
4692 case IDM_LoadOptions:
\r
4693 LoadOptionsPopup(hwnd);
\r
4696 case IDM_SaveOptions:
\r
4697 SaveOptionsPopup(hwnd);
\r
4700 case IDM_TimeControl:
\r
4701 TimeControlOptionsPopup(hwnd);
\r
4704 case IDM_SaveSettings:
\r
4705 SaveSettings(settingsFileName);
\r
4708 case IDM_SaveSettingsOnExit:
\r
4709 saveSettingsOnExit = !saveSettingsOnExit;
\r
4710 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4711 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4712 MF_CHECKED : MF_UNCHECKED));
\r
4723 case IDM_AboutGame:
\r
4728 appData.debugMode = !appData.debugMode;
\r
4729 if (appData.debugMode) {
\r
4730 char dir[MSG_SIZ];
\r
4731 GetCurrentDirectory(MSG_SIZ, dir);
\r
4732 SetCurrentDirectory(installDir);
\r
4733 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4734 SetCurrentDirectory(dir);
\r
4735 setbuf(debugFP, NULL);
\r
4742 case IDM_HELPCONTENTS:
\r
4743 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4744 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4745 MessageBox (GetFocus(),
\r
4746 "Unable to activate help",
\r
4747 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4751 case IDM_HELPSEARCH:
\r
4752 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4753 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4754 MessageBox (GetFocus(),
\r
4755 "Unable to activate help",
\r
4756 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4760 case IDM_HELPHELP:
\r
4761 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4762 MessageBox (GetFocus(),
\r
4763 "Unable to activate help",
\r
4764 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4769 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4771 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4772 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4773 FreeProcInstance(lpProc);
\r
4776 case IDM_DirectCommand1:
\r
4777 AskQuestionEvent("Direct Command",
\r
4778 "Send to chess program:", "", "1");
\r
4780 case IDM_DirectCommand2:
\r
4781 AskQuestionEvent("Direct Command",
\r
4782 "Send to second chess program:", "", "2");
\r
4785 case EP_WhitePawn:
\r
4786 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4787 fromX = fromY = -1;
\r
4790 case EP_WhiteKnight:
\r
4791 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4792 fromX = fromY = -1;
\r
4795 case EP_WhiteBishop:
\r
4796 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4797 fromX = fromY = -1;
\r
4800 case EP_WhiteRook:
\r
4801 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4802 fromX = fromY = -1;
\r
4805 case EP_WhiteQueen:
\r
4806 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4807 fromX = fromY = -1;
\r
4810 case EP_WhiteFerz:
\r
4811 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4812 fromX = fromY = -1;
\r
4815 case EP_WhiteWazir:
\r
4816 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4817 fromX = fromY = -1;
\r
4820 case EP_WhiteAlfil:
\r
4821 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4822 fromX = fromY = -1;
\r
4825 case EP_WhiteCannon:
\r
4826 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4827 fromX = fromY = -1;
\r
4830 case EP_WhiteCardinal:
\r
4831 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4832 fromX = fromY = -1;
\r
4835 case EP_WhiteMarshall:
\r
4836 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4837 fromX = fromY = -1;
\r
4840 case EP_WhiteKing:
\r
4841 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4842 fromX = fromY = -1;
\r
4845 case EP_BlackPawn:
\r
4846 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4847 fromX = fromY = -1;
\r
4850 case EP_BlackKnight:
\r
4851 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4852 fromX = fromY = -1;
\r
4855 case EP_BlackBishop:
\r
4856 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4857 fromX = fromY = -1;
\r
4860 case EP_BlackRook:
\r
4861 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4862 fromX = fromY = -1;
\r
4865 case EP_BlackQueen:
\r
4866 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4867 fromX = fromY = -1;
\r
4870 case EP_BlackFerz:
\r
4871 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4872 fromX = fromY = -1;
\r
4875 case EP_BlackWazir:
\r
4876 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4877 fromX = fromY = -1;
\r
4880 case EP_BlackAlfil:
\r
4881 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4882 fromX = fromY = -1;
\r
4885 case EP_BlackCannon:
\r
4886 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4887 fromX = fromY = -1;
\r
4890 case EP_BlackCardinal:
\r
4891 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4892 fromX = fromY = -1;
\r
4895 case EP_BlackMarshall:
\r
4896 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4897 fromX = fromY = -1;
\r
4900 case EP_BlackKing:
\r
4901 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4902 fromX = fromY = -1;
\r
4905 case EP_EmptySquare:
\r
4906 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4907 fromX = fromY = -1;
\r
4910 case EP_ClearBoard:
\r
4911 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4912 fromX = fromY = -1;
\r
4916 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4917 fromX = fromY = -1;
\r
4921 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4922 fromX = fromY = -1;
\r
4926 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4927 fromX = fromY = -1;
\r
4931 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4932 fromX = fromY = -1;
\r
4936 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4937 fromX = fromY = -1;
\r
4941 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4942 fromX = fromY = -1;
\r
4946 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4947 fromX = fromY = -1;
\r
4951 DropMenuEvent(WhiteRook, fromX, fromY);
\r
4952 fromX = fromY = -1;
\r
4956 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
4957 fromX = fromY = -1;
\r
4961 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4967 case CLOCK_TIMER_ID:
\r
4968 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
4969 clockTimerEvent = 0;
\r
4970 DecrementClocks(); /* call into back end */
\r
4972 case LOAD_GAME_TIMER_ID:
\r
4973 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
4974 loadGameTimerEvent = 0;
\r
4975 AutoPlayGameLoop(); /* call into back end */
\r
4977 case ANALYSIS_TIMER_ID:
\r
4978 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
4979 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
4980 AnalysisPeriodicEvent(0);
\r
4982 KillTimer(hwnd, analysisTimerEvent);
\r
4983 analysisTimerEvent = 0;
\r
4986 case DELAYED_TIMER_ID:
\r
4987 KillTimer(hwnd, delayedTimerEvent);
\r
4988 delayedTimerEvent = 0;
\r
4989 delayedTimerCallback();
\r
4994 case WM_USER_Input:
\r
4995 InputEvent(hwnd, message, wParam, lParam);
\r
4998 /* [AS] Also move "attached" child windows */
\r
4999 case WM_WINDOWPOSCHANGING:
\r
5001 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5002 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5004 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5005 /* Window is moving */
\r
5008 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5009 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5010 rcMain.right = wpMain.x + wpMain.width;
\r
5011 rcMain.top = wpMain.y;
\r
5012 rcMain.bottom = wpMain.y + wpMain.height;
\r
5014 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5015 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5016 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5017 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5018 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5019 wpMain.x = lpwp->x;
\r
5020 wpMain.y = lpwp->y;
\r
5025 /* [AS] Snapping */
\r
5026 case WM_ENTERSIZEMOVE:
\r
5027 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5028 if (hwnd == hwndMain) {
\r
5029 doingSizing = TRUE;
\r
5032 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5036 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5037 if (hwnd == hwndMain) {
\r
5038 lastSizing = wParam;
\r
5043 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5044 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5046 case WM_EXITSIZEMOVE:
\r
5047 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5048 if (hwnd == hwndMain) {
\r
5050 doingSizing = FALSE;
\r
5051 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5052 GetClientRect(hwnd, &client);
\r
5053 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5055 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5057 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5060 case WM_DESTROY: /* message: window being destroyed */
\r
5061 PostQuitMessage(0);
\r
5065 if (hwnd == hwndMain) {
\r
5070 default: /* Passes it on if unprocessed */
\r
5071 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5076 /*---------------------------------------------------------------------------*\
\r
5078 * Misc utility routines
\r
5080 \*---------------------------------------------------------------------------*/
\r
5083 * Decent random number generator, at least not as bad as Windows
\r
5084 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5086 unsigned int randstate;
\r
5091 randstate = randstate * 1664525 + 1013904223;
\r
5092 return (int) randstate & 0x7fffffff;
\r
5096 mysrandom(unsigned int seed)
\r
5103 * returns TRUE if user selects a different color, FALSE otherwise
\r
5107 ChangeColor(HWND hwnd, COLORREF *which)
\r
5109 static BOOL firstTime = TRUE;
\r
5110 static DWORD customColors[16];
\r
5112 COLORREF newcolor;
\r
5117 /* Make initial colors in use available as custom colors */
\r
5118 /* Should we put the compiled-in defaults here instead? */
\r
5120 customColors[i++] = lightSquareColor & 0xffffff;
\r
5121 customColors[i++] = darkSquareColor & 0xffffff;
\r
5122 customColors[i++] = whitePieceColor & 0xffffff;
\r
5123 customColors[i++] = blackPieceColor & 0xffffff;
\r
5124 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5125 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5127 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5128 customColors[i++] = textAttribs[ccl].color;
\r
5130 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5131 firstTime = FALSE;
\r
5134 cc.lStructSize = sizeof(cc);
\r
5135 cc.hwndOwner = hwnd;
\r
5136 cc.hInstance = NULL;
\r
5137 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5138 cc.lpCustColors = (LPDWORD) customColors;
\r
5139 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5141 if (!ChooseColor(&cc)) return FALSE;
\r
5143 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5144 if (newcolor == *which) return FALSE;
\r
5145 *which = newcolor;
\r
5149 InitDrawingColors();
\r
5150 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5155 MyLoadSound(MySound *ms)
\r
5161 if (ms->data) free(ms->data);
\r
5164 switch (ms->name[0]) {
\r
5170 /* System sound from Control Panel. Don't preload here. */
\r
5174 if (ms->name[1] == NULLCHAR) {
\r
5175 /* "!" alone = silence */
\r
5178 /* Builtin wave resource. Error if not found. */
\r
5179 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5180 if (h == NULL) break;
\r
5181 ms->data = (void *)LoadResource(hInst, h);
\r
5182 if (h == NULL) break;
\r
5187 /* .wav file. Error if not found. */
\r
5188 f = fopen(ms->name, "rb");
\r
5189 if (f == NULL) break;
\r
5190 if (fstat(fileno(f), &st) < 0) break;
\r
5191 ms->data = malloc(st.st_size);
\r
5192 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5198 char buf[MSG_SIZ];
\r
5199 sprintf(buf, "Error loading sound %s", ms->name);
\r
5200 DisplayError(buf, GetLastError());
\r
5206 MyPlaySound(MySound *ms)
\r
5208 BOOLEAN ok = FALSE;
\r
5210 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5211 switch (ms->name[0]) {
\r
5213 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5218 /* System sound from Control Panel (deprecated feature).
\r
5219 "$" alone or an unset sound name gets default beep (still in use). */
\r
5220 if (ms->name[1]) {
\r
5221 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5223 if (!ok) ok = MessageBeep(MB_OK);
\r
5226 /* Builtin wave resource, or "!" alone for silence */
\r
5227 if (ms->name[1]) {
\r
5228 if (ms->data == NULL) return FALSE;
\r
5229 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5235 /* .wav file. Error if not found. */
\r
5236 if (ms->data == NULL) return FALSE;
\r
5237 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5240 /* Don't print an error: this can happen innocently if the sound driver
\r
5241 is busy; for instance, if another instance of WinBoard is playing
\r
5242 a sound at about the same time. */
\r
5248 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5251 OPENFILENAME *ofn;
\r
5252 static UINT *number; /* gross that this is static */
\r
5254 switch (message) {
\r
5255 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5256 /* Center the dialog over the application window */
\r
5257 ofn = (OPENFILENAME *) lParam;
\r
5258 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5259 number = (UINT *) ofn->lCustData;
\r
5260 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5264 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5265 return FALSE; /* Allow for further processing */
\r
5268 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5269 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5271 return FALSE; /* Allow for further processing */
\r
5277 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5279 static UINT *number;
\r
5280 OPENFILENAME *ofname;
\r
5283 case WM_INITDIALOG:
\r
5284 ofname = (OPENFILENAME *)lParam;
\r
5285 number = (UINT *)(ofname->lCustData);
\r
5288 ofnot = (OFNOTIFY *)lParam;
\r
5289 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5290 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5299 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5300 char *nameFilt, char *dlgTitle, UINT *number,
\r
5301 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5303 OPENFILENAME openFileName;
\r
5304 char buf1[MSG_SIZ];
\r
5307 if (fileName == NULL) fileName = buf1;
\r
5308 if (defName == NULL) {
\r
5309 strcpy(fileName, "*.");
\r
5310 strcat(fileName, defExt);
\r
5312 strcpy(fileName, defName);
\r
5314 if (fileTitle) strcpy(fileTitle, "");
\r
5315 if (number) *number = 0;
\r
5317 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5318 openFileName.hwndOwner = hwnd;
\r
5319 openFileName.hInstance = (HANDLE) hInst;
\r
5320 openFileName.lpstrFilter = nameFilt;
\r
5321 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5322 openFileName.nMaxCustFilter = 0L;
\r
5323 openFileName.nFilterIndex = 1L;
\r
5324 openFileName.lpstrFile = fileName;
\r
5325 openFileName.nMaxFile = MSG_SIZ;
\r
5326 openFileName.lpstrFileTitle = fileTitle;
\r
5327 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5328 openFileName.lpstrInitialDir = NULL;
\r
5329 openFileName.lpstrTitle = dlgTitle;
\r
5330 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5331 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5332 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5333 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5334 openFileName.nFileOffset = 0;
\r
5335 openFileName.nFileExtension = 0;
\r
5336 openFileName.lpstrDefExt = defExt;
\r
5337 openFileName.lCustData = (LONG) number;
\r
5338 openFileName.lpfnHook = oldDialog ?
\r
5339 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5340 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5342 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5343 GetOpenFileName(&openFileName)) {
\r
5344 /* open the file */
\r
5345 f = fopen(openFileName.lpstrFile, write);
\r
5347 MessageBox(hwnd, "File open failed", NULL,
\r
5348 MB_OK|MB_ICONEXCLAMATION);
\r
5352 int err = CommDlgExtendedError();
\r
5353 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5362 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5364 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5367 * Get the first pop-up menu in the menu template. This is the
\r
5368 * menu that TrackPopupMenu displays.
\r
5370 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5372 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5375 * TrackPopup uses screen coordinates, so convert the
\r
5376 * coordinates of the mouse click to screen coordinates.
\r
5378 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5380 /* Draw and track the floating pop-up menu. */
\r
5381 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5382 pt.x, pt.y, 0, hwnd, NULL);
\r
5384 /* Destroy the menu.*/
\r
5385 DestroyMenu(hmenu);
\r
5390 int sizeX, sizeY, newSizeX, newSizeY;
\r
5392 } ResizeEditPlusButtonsClosure;
\r
5395 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5397 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5401 if (hChild == cl->hText) return TRUE;
\r
5402 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5403 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5404 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5405 ScreenToClient(cl->hDlg, &pt);
\r
5406 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5407 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5411 /* Resize a dialog that has a (rich) edit field filling most of
\r
5412 the top, with a row of buttons below */
\r
5414 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5417 int newTextHeight, newTextWidth;
\r
5418 ResizeEditPlusButtonsClosure cl;
\r
5420 /*if (IsIconic(hDlg)) return;*/
\r
5421 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5423 cl.hdwp = BeginDeferWindowPos(8);
\r
5425 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5426 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5427 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5428 if (newTextHeight < 0) {
\r
5429 newSizeY += -newTextHeight;
\r
5430 newTextHeight = 0;
\r
5432 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5433 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5439 cl.newSizeX = newSizeX;
\r
5440 cl.newSizeY = newSizeY;
\r
5441 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5443 EndDeferWindowPos(cl.hdwp);
\r
5446 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5448 RECT rChild, rParent;
\r
5449 int wChild, hChild, wParent, hParent;
\r
5450 int wScreen, hScreen, xNew, yNew;
\r
5453 /* Get the Height and Width of the child window */
\r
5454 GetWindowRect (hwndChild, &rChild);
\r
5455 wChild = rChild.right - rChild.left;
\r
5456 hChild = rChild.bottom - rChild.top;
\r
5458 /* Get the Height and Width of the parent window */
\r
5459 GetWindowRect (hwndParent, &rParent);
\r
5460 wParent = rParent.right - rParent.left;
\r
5461 hParent = rParent.bottom - rParent.top;
\r
5463 /* Get the display limits */
\r
5464 hdc = GetDC (hwndChild);
\r
5465 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5466 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5467 ReleaseDC(hwndChild, hdc);
\r
5469 /* Calculate new X position, then adjust for screen */
\r
5470 xNew = rParent.left + ((wParent - wChild) /2);
\r
5473 } else if ((xNew+wChild) > wScreen) {
\r
5474 xNew = wScreen - wChild;
\r
5477 /* Calculate new Y position, then adjust for screen */
\r
5479 yNew = rParent.top + ((hParent - hChild) /2);
\r
5482 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5487 } else if ((yNew+hChild) > hScreen) {
\r
5488 yNew = hScreen - hChild;
\r
5491 /* Set it, and return */
\r
5492 return SetWindowPos (hwndChild, NULL,
\r
5493 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5496 /* Center one window over another */
\r
5497 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5499 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5502 /*---------------------------------------------------------------------------*\
\r
5504 * Startup Dialog functions
\r
5506 \*---------------------------------------------------------------------------*/
\r
5508 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5510 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5512 while (*cd != NULL) {
\r
5513 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5519 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5521 char buf1[MAX_ARG_LEN];
\r
5524 if (str[0] == '@') {
\r
5525 FILE* f = fopen(str + 1, "r");
\r
5527 DisplayFatalError(str + 1, errno, 2);
\r
5530 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5532 buf1[len] = NULLCHAR;
\r
5536 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5539 char buf[MSG_SIZ];
\r
5540 char *end = strchr(str, '\n');
\r
5541 if (end == NULL) return;
\r
5542 memcpy(buf, str, end - str);
\r
5543 buf[end - str] = NULLCHAR;
\r
5544 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5550 SetStartupDialogEnables(HWND hDlg)
\r
5552 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5553 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5554 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5555 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5556 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5557 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5558 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5559 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5560 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5561 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5562 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5563 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5564 IsDlgButtonChecked(hDlg, OPT_View));
\r
5568 QuoteForFilename(char *filename)
\r
5570 int dquote, space;
\r
5571 dquote = strchr(filename, '"') != NULL;
\r
5572 space = strchr(filename, ' ') != NULL;
\r
5573 if (dquote || space) {
\r
5585 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5587 char buf[MSG_SIZ];
\r
5590 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5591 q = QuoteForFilename(nthcp);
\r
5592 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5593 if (*nthdir != NULLCHAR) {
\r
5594 q = QuoteForFilename(nthdir);
\r
5595 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5597 if (*nthcp == NULLCHAR) {
\r
5598 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5599 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5600 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5601 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5606 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5608 char buf[MSG_SIZ];
\r
5612 switch (message) {
\r
5613 case WM_INITDIALOG:
\r
5614 /* Center the dialog */
\r
5615 CenterWindow (hDlg, GetDesktopWindow());
\r
5616 /* Initialize the dialog items */
\r
5617 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5618 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5619 firstChessProgramNames);
\r
5620 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5621 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5622 secondChessProgramNames);
\r
5623 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5624 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5625 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5626 if (*appData.icsHelper != NULLCHAR) {
\r
5627 char *q = QuoteForFilename(appData.icsHelper);
\r
5628 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5630 if (*appData.icsHost == NULLCHAR) {
\r
5631 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5632 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5633 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5634 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5635 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5638 if (appData.icsActive) {
\r
5639 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5641 else if (appData.noChessProgram) {
\r
5642 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5645 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5648 SetStartupDialogEnables(hDlg);
\r
5652 switch (LOWORD(wParam)) {
\r
5654 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5655 strcpy(buf, "/fcp=");
\r
5656 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5658 ParseArgs(StringGet, &p);
\r
5659 strcpy(buf, "/scp=");
\r
5660 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5662 ParseArgs(StringGet, &p);
\r
5663 appData.noChessProgram = FALSE;
\r
5664 appData.icsActive = FALSE;
\r
5665 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5666 strcpy(buf, "/ics /icshost=");
\r
5667 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5669 ParseArgs(StringGet, &p);
\r
5670 if (appData.zippyPlay) {
\r
5671 strcpy(buf, "/fcp=");
\r
5672 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5674 ParseArgs(StringGet, &p);
\r
5676 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5677 appData.noChessProgram = TRUE;
\r
5678 appData.icsActive = FALSE;
\r
5680 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5681 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5684 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5685 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5687 ParseArgs(StringGet, &p);
\r
5689 EndDialog(hDlg, TRUE);
\r
5696 case IDM_HELPCONTENTS:
\r
5697 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5698 MessageBox (GetFocus(),
\r
5699 "Unable to activate help",
\r
5700 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5705 SetStartupDialogEnables(hDlg);
\r
5713 /*---------------------------------------------------------------------------*\
\r
5715 * About box dialog functions
\r
5717 \*---------------------------------------------------------------------------*/
\r
5719 /* Process messages for "About" dialog box */
\r
5721 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5723 switch (message) {
\r
5724 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5725 /* Center the dialog over the application window */
\r
5726 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5727 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5731 case WM_COMMAND: /* message: received a command */
\r
5732 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5733 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5734 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5742 /*---------------------------------------------------------------------------*\
\r
5744 * Comment Dialog functions
\r
5746 \*---------------------------------------------------------------------------*/
\r
5749 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5751 static HANDLE hwndText = NULL;
\r
5752 int len, newSizeX, newSizeY, flags;
\r
5753 static int sizeX, sizeY;
\r
5758 switch (message) {
\r
5759 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5760 /* Initialize the dialog items */
\r
5761 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5762 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5763 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5764 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5765 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5766 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5767 SetWindowText(hDlg, commentTitle);
\r
5768 if (editComment) {
\r
5769 SetFocus(hwndText);
\r
5771 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5773 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5774 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5775 MAKELPARAM(FALSE, 0));
\r
5776 /* Size and position the dialog */
\r
5777 if (!commentDialog) {
\r
5778 commentDialog = hDlg;
\r
5779 flags = SWP_NOZORDER;
\r
5780 GetClientRect(hDlg, &rect);
\r
5781 sizeX = rect.right;
\r
5782 sizeY = rect.bottom;
\r
5783 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5784 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5785 WINDOWPLACEMENT wp;
\r
5786 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5787 wp.length = sizeof(WINDOWPLACEMENT);
\r
5789 wp.showCmd = SW_SHOW;
\r
5790 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5791 wp.rcNormalPosition.left = wpComment.x;
\r
5792 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5793 wp.rcNormalPosition.top = wpComment.y;
\r
5794 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5795 SetWindowPlacement(hDlg, &wp);
\r
5797 GetClientRect(hDlg, &rect);
\r
5798 newSizeX = rect.right;
\r
5799 newSizeY = rect.bottom;
\r
5800 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5801 newSizeX, newSizeY);
\r
5808 case WM_COMMAND: /* message: received a command */
\r
5809 switch (LOWORD(wParam)) {
\r
5811 if (editComment) {
\r
5813 /* Read changed options from the dialog box */
\r
5814 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5815 len = GetWindowTextLength(hwndText);
\r
5816 str = (char *) malloc(len + 1);
\r
5817 GetWindowText(hwndText, str, len + 1);
\r
5826 ReplaceComment(commentIndex, str);
\r
5833 case OPT_CancelComment:
\r
5837 case OPT_ClearComment:
\r
5838 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5841 case OPT_EditComment:
\r
5842 EditCommentEvent();
\r
5851 newSizeX = LOWORD(lParam);
\r
5852 newSizeY = HIWORD(lParam);
\r
5853 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5858 case WM_GETMINMAXINFO:
\r
5859 /* Prevent resizing window too small */
\r
5860 mmi = (MINMAXINFO *) lParam;
\r
5861 mmi->ptMinTrackSize.x = 100;
\r
5862 mmi->ptMinTrackSize.y = 100;
\r
5869 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5874 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5876 if (str == NULL) str = "";
\r
5877 p = (char *) malloc(2 * strlen(str) + 2);
\r
5880 if (*str == '\n') *q++ = '\r';
\r
5884 if (commentText != NULL) free(commentText);
\r
5886 commentIndex = index;
\r
5887 commentTitle = title;
\r
5889 editComment = edit;
\r
5891 if (commentDialog) {
\r
5892 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5893 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5895 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5896 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5897 hwndMain, (DLGPROC)lpProc);
\r
5898 FreeProcInstance(lpProc);
\r
5904 /*---------------------------------------------------------------------------*\
\r
5906 * Type-in move dialog functions
\r
5908 \*---------------------------------------------------------------------------*/
\r
5911 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5913 char move[MSG_SIZ];
\r
5915 ChessMove moveType;
\r
5916 int fromX, fromY, toX, toY;
\r
5919 switch (message) {
\r
5920 case WM_INITDIALOG:
\r
5921 move[0] = (char) lParam;
\r
5922 move[1] = NULLCHAR;
\r
5923 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5924 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5925 SetWindowText(hInput, move);
\r
5927 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5931 switch (LOWORD(wParam)) {
\r
5933 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5934 { int n; Board board;
\r
5936 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5937 EditPositionPasteFEN(move);
\r
5938 EndDialog(hDlg, TRUE);
\r
5941 // [HGM] movenum: allow move number to be typed in any mode
\r
5942 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5944 EndDialog(hDlg, TRUE);
\r
5948 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5949 gameMode != Training) {
\r
5950 DisplayMoveError("Displayed move is not current");
\r
5952 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
5953 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5954 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
5955 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
5956 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5957 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
5958 if (gameMode != Training)
\r
5959 forwardMostMove = currentMove;
\r
5960 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
5962 DisplayMoveError("Could not parse move");
\r
5965 EndDialog(hDlg, TRUE);
\r
5968 EndDialog(hDlg, FALSE);
\r
5979 PopUpMoveDialog(char firstchar)
\r
5983 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
5984 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
5985 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
5986 gameMode == EditPosition || gameMode == IcsExamining ||
\r
5987 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
5988 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
5989 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
5990 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
5991 gameMode == Training) {
\r
5992 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
5993 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
5994 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
5995 FreeProcInstance(lpProc);
\r
5999 /*---------------------------------------------------------------------------*\
\r
6001 * Type-in name dialog functions
\r
6003 \*---------------------------------------------------------------------------*/
\r
6006 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6008 char move[MSG_SIZ];
\r
6011 switch (message) {
\r
6012 case WM_INITDIALOG:
\r
6013 move[0] = (char) lParam;
\r
6014 move[1] = NULLCHAR;
\r
6015 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6016 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6017 SetWindowText(hInput, move);
\r
6019 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6023 switch (LOWORD(wParam)) {
\r
6025 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6026 appData.userName = strdup(move);
\r
6029 EndDialog(hDlg, TRUE);
\r
6032 EndDialog(hDlg, FALSE);
\r
6043 PopUpNameDialog(char firstchar)
\r
6047 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6048 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6049 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6050 FreeProcInstance(lpProc);
\r
6053 /*---------------------------------------------------------------------------*\
\r
6057 \*---------------------------------------------------------------------------*/
\r
6059 /* Nonmodal error box */
\r
6060 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6061 WPARAM wParam, LPARAM lParam);
\r
6064 ErrorPopUp(char *title, char *content)
\r
6068 BOOLEAN modal = hwndMain == NULL;
\r
6086 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6087 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6090 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6092 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6093 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6094 hwndMain, (DLGPROC)lpProc);
\r
6095 FreeProcInstance(lpProc);
\r
6102 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6103 if (errorDialog == NULL) return;
\r
6104 DestroyWindow(errorDialog);
\r
6105 errorDialog = NULL;
\r
6106 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6110 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6115 switch (message) {
\r
6116 case WM_INITDIALOG:
\r
6117 GetWindowRect(hDlg, &rChild);
\r
6120 SetWindowPos(hDlg, NULL, rChild.left,
\r
6121 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6122 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6126 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6127 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6128 and it doesn't work when you resize the dialog.
\r
6129 For now, just give it a default position.
\r
6131 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6133 errorDialog = hDlg;
\r
6134 SetWindowText(hDlg, errorTitle);
\r
6135 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6136 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6140 switch (LOWORD(wParam)) {
\r
6143 if (errorDialog == hDlg) errorDialog = NULL;
\r
6144 DestroyWindow(hDlg);
\r
6156 HWND gothicDialog = NULL;
\r
6159 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6163 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6165 switch (message) {
\r
6166 case WM_INITDIALOG:
\r
6167 GetWindowRect(hDlg, &rChild);
\r
6169 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6173 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6174 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6175 and it doesn't work when you resize the dialog.
\r
6176 For now, just give it a default position.
\r
6178 gothicDialog = hDlg;
\r
6179 SetWindowText(hDlg, errorTitle);
\r
6180 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6181 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6185 switch (LOWORD(wParam)) {
\r
6188 if (errorDialog == hDlg) errorDialog = NULL;
\r
6189 DestroyWindow(hDlg);
\r
6201 GothicPopUp(char *title, VariantClass variant)
\r
6204 static char *lastTitle;
\r
6206 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6207 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6209 if(lastTitle != title && gothicDialog != NULL) {
\r
6210 DestroyWindow(gothicDialog);
\r
6211 gothicDialog = NULL;
\r
6213 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6214 title = lastTitle;
\r
6215 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6216 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6217 hwndMain, (DLGPROC)lpProc);
\r
6218 FreeProcInstance(lpProc);
\r
6223 /*---------------------------------------------------------------------------*\
\r
6225 * Ics Interaction console functions
\r
6227 \*---------------------------------------------------------------------------*/
\r
6229 #define HISTORY_SIZE 64
\r
6230 static char *history[HISTORY_SIZE];
\r
6231 int histIn = 0, histP = 0;
\r
6234 SaveInHistory(char *cmd)
\r
6236 if (history[histIn] != NULL) {
\r
6237 free(history[histIn]);
\r
6238 history[histIn] = NULL;
\r
6240 if (*cmd == NULLCHAR) return;
\r
6241 history[histIn] = StrSave(cmd);
\r
6242 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6243 if (history[histIn] != NULL) {
\r
6244 free(history[histIn]);
\r
6245 history[histIn] = NULL;
\r
6251 PrevInHistory(char *cmd)
\r
6254 if (histP == histIn) {
\r
6255 if (history[histIn] != NULL) free(history[histIn]);
\r
6256 history[histIn] = StrSave(cmd);
\r
6258 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6259 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6261 return history[histP];
\r
6267 if (histP == histIn) return NULL;
\r
6268 histP = (histP + 1) % HISTORY_SIZE;
\r
6269 return history[histP];
\r
6273 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6277 hmenu = LoadMenu(hInst, "TextMenu");
\r
6278 h = GetSubMenu(hmenu, 0);
\r
6280 if (strcmp(e->item, "-") == 0) {
\r
6281 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6283 if (e->item[0] == '|') {
\r
6284 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
\r
6285 IDM_CommandX + i, &e->item[1]);
\r
6287 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
\r
6296 WNDPROC consoleTextWindowProc;
\r
6299 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6301 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6302 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6306 SetWindowText(hInput, command);
\r
6308 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6310 sel.cpMin = 999999;
\r
6311 sel.cpMax = 999999;
\r
6312 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6317 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6318 if (sel.cpMin == sel.cpMax) {
\r
6319 /* Expand to surrounding word */
\r
6322 tr.chrg.cpMax = sel.cpMin;
\r
6323 tr.chrg.cpMin = --sel.cpMin;
\r
6324 if (sel.cpMin < 0) break;
\r
6325 tr.lpstrText = name;
\r
6326 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6327 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6331 tr.chrg.cpMin = sel.cpMax;
\r
6332 tr.chrg.cpMax = ++sel.cpMax;
\r
6333 tr.lpstrText = name;
\r
6334 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6335 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6338 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6339 MessageBeep(MB_ICONEXCLAMATION);
\r
6343 tr.lpstrText = name;
\r
6344 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6346 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6347 MessageBeep(MB_ICONEXCLAMATION);
\r
6350 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6353 sprintf(buf, "%s %s", command, name);
\r
6354 SetWindowText(hInput, buf);
\r
6355 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6357 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6358 SetWindowText(hInput, buf);
\r
6359 sel.cpMin = 999999;
\r
6360 sel.cpMax = 999999;
\r
6361 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6367 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6372 switch (message) {
\r
6374 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6377 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6380 sel.cpMin = 999999;
\r
6381 sel.cpMax = 999999;
\r
6382 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6383 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6388 if(wParam != '\022') {
\r
6389 if (wParam == '\t') {
\r
6390 if (GetKeyState(VK_SHIFT) < 0) {
\r
6392 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6393 if (buttonDesc[0].hwnd) {
\r
6394 SetFocus(buttonDesc[0].hwnd);
\r
6396 SetFocus(hwndMain);
\r
6400 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6403 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6404 JAWS_DELETE( SetFocus(hInput); )
\r
6405 SendMessage(hInput, message, wParam, lParam);
\r
6408 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6409 case WM_RBUTTONUP:
\r
6410 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6411 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6412 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6415 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6416 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6417 if (sel.cpMin == sel.cpMax) {
\r
6418 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6419 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6421 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6422 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6424 pt.x = LOWORD(lParam);
\r
6425 pt.y = HIWORD(lParam);
\r
6426 MenuPopup(hwnd, pt, hmenu, -1);
\r
6430 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6432 return SendMessage(hInput, message, wParam, lParam);
\r
6433 case WM_MBUTTONDOWN:
\r
6434 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6435 case WM_RBUTTONDOWN:
\r
6436 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6437 /* Move selection here if it was empty */
\r
6439 pt.x = LOWORD(lParam);
\r
6440 pt.y = HIWORD(lParam);
\r
6441 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6442 if (sel.cpMin == sel.cpMax) {
\r
6443 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6444 sel.cpMax = sel.cpMin;
\r
6445 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6447 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6451 switch (LOWORD(wParam)) {
\r
6452 case IDM_QuickPaste:
\r
6454 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6455 if (sel.cpMin == sel.cpMax) {
\r
6456 MessageBeep(MB_ICONEXCLAMATION);
\r
6459 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6460 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6461 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6466 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6469 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6472 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6476 int i = LOWORD(wParam) - IDM_CommandX;
\r
6477 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6478 icsTextMenuEntry[i].command != NULL) {
\r
6479 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6480 icsTextMenuEntry[i].getname,
\r
6481 icsTextMenuEntry[i].immediate);
\r
6489 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6492 WNDPROC consoleInputWindowProc;
\r
6495 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6497 char buf[MSG_SIZ];
\r
6499 static BOOL sendNextChar = FALSE;
\r
6500 static BOOL quoteNextChar = FALSE;
\r
6501 InputSource *is = consoleInputSource;
\r
6505 switch (message) {
\r
6507 if (!appData.localLineEditing || sendNextChar) {
\r
6508 is->buf[0] = (CHAR) wParam;
\r
6510 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6511 sendNextChar = FALSE;
\r
6514 if (quoteNextChar) {
\r
6515 buf[0] = (char) wParam;
\r
6516 buf[1] = NULLCHAR;
\r
6517 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6518 quoteNextChar = FALSE;
\r
6522 case '\r': /* Enter key */
\r
6523 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6524 if (consoleEcho) SaveInHistory(is->buf);
\r
6525 is->buf[is->count++] = '\n';
\r
6526 is->buf[is->count] = NULLCHAR;
\r
6527 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6528 if (consoleEcho) {
\r
6529 ConsoleOutput(is->buf, is->count, TRUE);
\r
6530 } else if (appData.localLineEditing) {
\r
6531 ConsoleOutput("\n", 1, TRUE);
\r
6534 case '\033': /* Escape key */
\r
6535 SetWindowText(hwnd, "");
\r
6536 cf.cbSize = sizeof(CHARFORMAT);
\r
6537 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6538 if (consoleEcho) {
\r
6539 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6541 cf.crTextColor = COLOR_ECHOOFF;
\r
6543 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6544 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6546 case '\t': /* Tab key */
\r
6547 if (GetKeyState(VK_SHIFT) < 0) {
\r
6549 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6552 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6553 if (buttonDesc[0].hwnd) {
\r
6554 SetFocus(buttonDesc[0].hwnd);
\r
6556 SetFocus(hwndMain);
\r
6560 case '\023': /* Ctrl+S */
\r
6561 sendNextChar = TRUE;
\r
6563 case '\021': /* Ctrl+Q */
\r
6564 quoteNextChar = TRUE;
\r
6574 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6575 p = PrevInHistory(buf);
\r
6577 SetWindowText(hwnd, p);
\r
6578 sel.cpMin = 999999;
\r
6579 sel.cpMax = 999999;
\r
6580 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6585 p = NextInHistory();
\r
6587 SetWindowText(hwnd, p);
\r
6588 sel.cpMin = 999999;
\r
6589 sel.cpMax = 999999;
\r
6590 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6596 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6600 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6604 case WM_MBUTTONDOWN:
\r
6605 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6606 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6608 case WM_RBUTTONUP:
\r
6609 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6610 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6611 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6615 hmenu = LoadMenu(hInst, "InputMenu");
\r
6616 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6617 if (sel.cpMin == sel.cpMax) {
\r
6618 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6619 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6621 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6622 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6624 pt.x = LOWORD(lParam);
\r
6625 pt.y = HIWORD(lParam);
\r
6626 MenuPopup(hwnd, pt, hmenu, -1);
\r
6630 switch (LOWORD(wParam)) {
\r
6632 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6634 case IDM_SelectAll:
\r
6636 sel.cpMax = -1; /*999999?*/
\r
6637 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6640 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6643 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6646 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6651 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6654 #define CO_MAX 100000
\r
6655 #define CO_TRIM 1000
\r
6658 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6660 static SnapData sd;
\r
6661 HWND hText, hInput;
\r
6663 static int sizeX, sizeY;
\r
6664 int newSizeX, newSizeY;
\r
6668 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6669 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6671 switch (message) {
\r
6673 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6675 ENLINK *pLink = (ENLINK*)lParam;
\r
6676 if (pLink->msg == WM_LBUTTONUP)
\r
6680 tr.chrg = pLink->chrg;
\r
6681 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6682 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6683 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6684 free(tr.lpstrText);
\r
6688 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6689 hwndConsole = hDlg;
\r
6691 consoleTextWindowProc = (WNDPROC)
\r
6692 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6693 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6694 consoleInputWindowProc = (WNDPROC)
\r
6695 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6696 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6697 Colorize(ColorNormal, TRUE);
\r
6698 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6699 ChangedConsoleFont();
\r
6700 GetClientRect(hDlg, &rect);
\r
6701 sizeX = rect.right;
\r
6702 sizeY = rect.bottom;
\r
6703 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6704 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6705 WINDOWPLACEMENT wp;
\r
6706 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6707 wp.length = sizeof(WINDOWPLACEMENT);
\r
6709 wp.showCmd = SW_SHOW;
\r
6710 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6711 wp.rcNormalPosition.left = wpConsole.x;
\r
6712 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6713 wp.rcNormalPosition.top = wpConsole.y;
\r
6714 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6715 SetWindowPlacement(hDlg, &wp);
\r
6718 // [HGM] Chessknight's change 2004-07-13
\r
6719 else { /* Determine Defaults */
\r
6720 WINDOWPLACEMENT wp;
\r
6721 wpConsole.x = wpMain.width + 1;
\r
6722 wpConsole.y = wpMain.y;
\r
6723 wpConsole.width = screenWidth - wpMain.width;
\r
6724 wpConsole.height = wpMain.height;
\r
6725 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6726 wp.length = sizeof(WINDOWPLACEMENT);
\r
6728 wp.showCmd = SW_SHOW;
\r
6729 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6730 wp.rcNormalPosition.left = wpConsole.x;
\r
6731 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6732 wp.rcNormalPosition.top = wpConsole.y;
\r
6733 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6734 SetWindowPlacement(hDlg, &wp);
\r
6737 // Allow hText to highlight URLs and send notifications on them
\r
6738 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6739 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6740 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6741 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6755 if (IsIconic(hDlg)) break;
\r
6756 newSizeX = LOWORD(lParam);
\r
6757 newSizeY = HIWORD(lParam);
\r
6758 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6759 RECT rectText, rectInput;
\r
6761 int newTextHeight, newTextWidth;
\r
6762 GetWindowRect(hText, &rectText);
\r
6763 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6764 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6765 if (newTextHeight < 0) {
\r
6766 newSizeY += -newTextHeight;
\r
6767 newTextHeight = 0;
\r
6769 SetWindowPos(hText, NULL, 0, 0,
\r
6770 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6771 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6772 pt.x = rectInput.left;
\r
6773 pt.y = rectInput.top + newSizeY - sizeY;
\r
6774 ScreenToClient(hDlg, &pt);
\r
6775 SetWindowPos(hInput, NULL,
\r
6776 pt.x, pt.y, /* needs client coords */
\r
6777 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6778 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6784 case WM_GETMINMAXINFO:
\r
6785 /* Prevent resizing window too small */
\r
6786 mmi = (MINMAXINFO *) lParam;
\r
6787 mmi->ptMinTrackSize.x = 100;
\r
6788 mmi->ptMinTrackSize.y = 100;
\r
6791 /* [AS] Snapping */
\r
6792 case WM_ENTERSIZEMOVE:
\r
6793 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6796 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6799 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6801 case WM_EXITSIZEMOVE:
\r
6802 UpdateICSWidth(hText);
\r
6803 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6806 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6814 if (hwndConsole) return;
\r
6815 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6816 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6821 ConsoleOutput(char* data, int length, int forceVisible)
\r
6826 char buf[CO_MAX+1];
\r
6829 static int delayLF = 0;
\r
6830 CHARRANGE savesel, sel;
\r
6832 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6840 while (length--) {
\r
6848 } else if (*p == '\007') {
\r
6849 MyPlaySound(&sounds[(int)SoundBell]);
\r
6856 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6857 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6858 /* Save current selection */
\r
6859 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6860 exlen = GetWindowTextLength(hText);
\r
6861 /* Find out whether current end of text is visible */
\r
6862 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6863 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6864 /* Trim existing text if it's too long */
\r
6865 if (exlen + (q - buf) > CO_MAX) {
\r
6866 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6869 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6870 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6872 savesel.cpMin -= trim;
\r
6873 savesel.cpMax -= trim;
\r
6874 if (exlen < 0) exlen = 0;
\r
6875 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6876 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6878 /* Append the new text */
\r
6879 sel.cpMin = exlen;
\r
6880 sel.cpMax = exlen;
\r
6881 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6882 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6883 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6884 if (forceVisible || exlen == 0 ||
\r
6885 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6886 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6887 /* Scroll to make new end of text visible if old end of text
\r
6888 was visible or new text is an echo of user typein */
\r
6889 sel.cpMin = 9999999;
\r
6890 sel.cpMax = 9999999;
\r
6891 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6892 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6893 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6894 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6896 if (savesel.cpMax == exlen || forceVisible) {
\r
6897 /* Move insert point to new end of text if it was at the old
\r
6898 end of text or if the new text is an echo of user typein */
\r
6899 sel.cpMin = 9999999;
\r
6900 sel.cpMax = 9999999;
\r
6901 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6903 /* Restore previous selection */
\r
6904 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6906 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6913 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6917 COLORREF oldFg, oldBg;
\r
6921 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6923 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6924 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6925 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6928 rect.right = x + squareSize;
\r
6930 rect.bottom = y + squareSize;
\r
6933 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6934 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6935 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6936 &rect, str, strlen(str), NULL);
\r
6938 (void) SetTextColor(hdc, oldFg);
\r
6939 (void) SetBkColor(hdc, oldBg);
\r
6940 (void) SelectObject(hdc, oldFont);
\r
6944 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6945 RECT *rect, char *color, char *flagFell)
\r
6949 COLORREF oldFg, oldBg;
\r
6952 if (appData.clockMode) {
\r
6954 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
6956 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
6963 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6964 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6966 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
6967 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
6969 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6973 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6974 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
6975 rect, str, strlen(str), NULL);
\r
6976 if(logoHeight > 0 && appData.clockMode) {
\r
6978 sprintf(buf, "%s %s", buf+7, flagFell);
\r
6979 r.top = rect->top + logoHeight/2;
\r
6980 r.left = rect->left;
\r
6981 r.right = rect->right;
\r
6982 r.bottom = rect->bottom;
\r
6983 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6984 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
6985 &r, str, strlen(str), NULL);
\r
6987 (void) SetTextColor(hdc, oldFg);
\r
6988 (void) SetBkColor(hdc, oldBg);
\r
6989 (void) SelectObject(hdc, oldFont);
\r
6994 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7000 if( count <= 0 ) {
\r
7001 if (appData.debugMode) {
\r
7002 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7005 return ERROR_INVALID_USER_BUFFER;
\r
7008 ResetEvent(ovl->hEvent);
\r
7009 ovl->Offset = ovl->OffsetHigh = 0;
\r
7010 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7014 err = GetLastError();
\r
7015 if (err == ERROR_IO_PENDING) {
\r
7016 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7020 err = GetLastError();
\r
7027 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7032 ResetEvent(ovl->hEvent);
\r
7033 ovl->Offset = ovl->OffsetHigh = 0;
\r
7034 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7038 err = GetLastError();
\r
7039 if (err == ERROR_IO_PENDING) {
\r
7040 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7044 err = GetLastError();
\r
7050 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7051 void CheckForInputBufferFull( InputSource * is )
\r
7053 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7054 /* Look for end of line */
\r
7055 char * p = is->buf;
\r
7057 while( p < is->next && *p != '\n' ) {
\r
7061 if( p >= is->next ) {
\r
7062 if (appData.debugMode) {
\r
7063 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7066 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7067 is->count = (DWORD) -1;
\r
7068 is->next = is->buf;
\r
7074 InputThread(LPVOID arg)
\r
7079 is = (InputSource *) arg;
\r
7080 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7081 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7082 while (is->hThread != NULL) {
\r
7083 is->error = DoReadFile(is->hFile, is->next,
\r
7084 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7085 &is->count, &ovl);
\r
7086 if (is->error == NO_ERROR) {
\r
7087 is->next += is->count;
\r
7089 if (is->error == ERROR_BROKEN_PIPE) {
\r
7090 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7093 is->count = (DWORD) -1;
\r
7094 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7099 CheckForInputBufferFull( is );
\r
7101 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7103 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7105 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7108 CloseHandle(ovl.hEvent);
\r
7109 CloseHandle(is->hFile);
\r
7111 if (appData.debugMode) {
\r
7112 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7119 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7121 NonOvlInputThread(LPVOID arg)
\r
7128 is = (InputSource *) arg;
\r
7129 while (is->hThread != NULL) {
\r
7130 is->error = ReadFile(is->hFile, is->next,
\r
7131 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7132 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7133 if (is->error == NO_ERROR) {
\r
7134 /* Change CRLF to LF */
\r
7135 if (is->next > is->buf) {
\r
7137 i = is->count + 1;
\r
7145 if (prev == '\r' && *p == '\n') {
\r
7157 if (is->error == ERROR_BROKEN_PIPE) {
\r
7158 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7161 is->count = (DWORD) -1;
\r
7165 CheckForInputBufferFull( is );
\r
7167 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7169 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7171 if (is->count < 0) break; /* Quit on error */
\r
7173 CloseHandle(is->hFile);
\r
7178 SocketInputThread(LPVOID arg)
\r
7182 is = (InputSource *) arg;
\r
7183 while (is->hThread != NULL) {
\r
7184 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7185 if ((int)is->count == SOCKET_ERROR) {
\r
7186 is->count = (DWORD) -1;
\r
7187 is->error = WSAGetLastError();
\r
7189 is->error = NO_ERROR;
\r
7190 is->next += is->count;
\r
7191 if (is->count == 0 && is->second == is) {
\r
7192 /* End of file on stderr; quit with no message */
\r
7196 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7198 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7200 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7206 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7210 is = (InputSource *) lParam;
\r
7211 if (is->lineByLine) {
\r
7212 /* Feed in lines one by one */
\r
7213 char *p = is->buf;
\r
7215 while (q < is->next) {
\r
7216 if (*q++ == '\n') {
\r
7217 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7222 /* Move any partial line to the start of the buffer */
\r
7224 while (p < is->next) {
\r
7229 if (is->error != NO_ERROR || is->count == 0) {
\r
7230 /* Notify backend of the error. Note: If there was a partial
\r
7231 line at the end, it is not flushed through. */
\r
7232 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7235 /* Feed in the whole chunk of input at once */
\r
7236 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7237 is->next = is->buf;
\r
7241 /*---------------------------------------------------------------------------*\
\r
7243 * Menu enables. Used when setting various modes.
\r
7245 \*---------------------------------------------------------------------------*/
\r
7253 GreyRevert(Boolean grey)
\r
7254 { // [HGM] vari: for retracting variations in local mode
\r
7255 HMENU hmenu = GetMenu(hwndMain);
\r
7256 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7260 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7262 while (enab->item > 0) {
\r
7263 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7268 Enables gnuEnables[] = {
\r
7269 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7270 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7271 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7272 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7273 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7274 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7275 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7276 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7277 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7278 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7279 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7283 Enables icsEnables[] = {
\r
7284 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7285 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7286 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7287 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7288 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7289 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7290 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7291 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7292 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7293 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7294 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7295 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7296 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7297 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7298 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7303 Enables zippyEnables[] = {
\r
7304 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7305 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7306 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7307 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7312 Enables ncpEnables[] = {
\r
7313 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7314 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7315 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7316 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7317 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7318 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7319 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7320 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7321 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7322 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7323 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7324 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7325 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7326 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7327 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7328 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7329 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7330 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7331 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7335 Enables trainingOnEnables[] = {
\r
7336 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7337 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7338 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7339 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7340 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7341 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7342 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7343 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7347 Enables trainingOffEnables[] = {
\r
7348 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7349 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7350 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7351 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7352 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7353 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7354 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7355 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7359 /* These modify either ncpEnables or gnuEnables */
\r
7360 Enables cmailEnables[] = {
\r
7361 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7362 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7363 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7364 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7365 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7366 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7367 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7371 Enables machineThinkingEnables[] = {
\r
7372 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7373 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7374 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7375 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7376 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7377 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7378 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7379 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7380 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7381 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7382 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7383 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7384 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7385 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7386 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7390 Enables userThinkingEnables[] = {
\r
7391 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7392 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7393 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7394 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7395 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7396 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7397 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7398 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7399 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7400 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7401 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7402 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7403 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7404 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7405 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7409 /*---------------------------------------------------------------------------*\
\r
7411 * Front-end interface functions exported by XBoard.
\r
7412 * Functions appear in same order as prototypes in frontend.h.
\r
7414 \*---------------------------------------------------------------------------*/
\r
7418 static UINT prevChecked = 0;
\r
7419 static int prevPausing = 0;
\r
7422 if (pausing != prevPausing) {
\r
7423 prevPausing = pausing;
\r
7424 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7425 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7426 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7429 switch (gameMode) {
\r
7430 case BeginningOfGame:
\r
7431 if (appData.icsActive)
\r
7432 nowChecked = IDM_IcsClient;
\r
7433 else if (appData.noChessProgram)
\r
7434 nowChecked = IDM_EditGame;
\r
7436 nowChecked = IDM_MachineBlack;
\r
7438 case MachinePlaysBlack:
\r
7439 nowChecked = IDM_MachineBlack;
\r
7441 case MachinePlaysWhite:
\r
7442 nowChecked = IDM_MachineWhite;
\r
7444 case TwoMachinesPlay:
\r
7445 nowChecked = IDM_TwoMachines;
\r
7448 nowChecked = IDM_AnalysisMode;
\r
7451 nowChecked = IDM_AnalyzeFile;
\r
7454 nowChecked = IDM_EditGame;
\r
7456 case PlayFromGameFile:
\r
7457 nowChecked = IDM_LoadGame;
\r
7459 case EditPosition:
\r
7460 nowChecked = IDM_EditPosition;
\r
7463 nowChecked = IDM_Training;
\r
7465 case IcsPlayingWhite:
\r
7466 case IcsPlayingBlack:
\r
7467 case IcsObserving:
\r
7469 nowChecked = IDM_IcsClient;
\r
7476 if (prevChecked != 0)
\r
7477 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7478 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7479 if (nowChecked != 0)
\r
7480 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7481 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7483 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7484 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7485 MF_BYCOMMAND|MF_ENABLED);
\r
7487 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7488 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7491 prevChecked = nowChecked;
\r
7493 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7494 if (appData.icsActive) {
\r
7495 if (appData.icsEngineAnalyze) {
\r
7496 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7497 MF_BYCOMMAND|MF_CHECKED);
\r
7499 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7500 MF_BYCOMMAND|MF_UNCHECKED);
\r
7508 HMENU hmenu = GetMenu(hwndMain);
\r
7509 SetMenuEnables(hmenu, icsEnables);
\r
7510 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7511 MF_BYPOSITION|MF_ENABLED);
\r
7513 if (appData.zippyPlay) {
\r
7514 SetMenuEnables(hmenu, zippyEnables);
\r
7515 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7516 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7517 MF_BYCOMMAND|MF_ENABLED);
\r
7525 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7531 HMENU hmenu = GetMenu(hwndMain);
\r
7532 SetMenuEnables(hmenu, ncpEnables);
\r
7533 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7534 MF_BYPOSITION|MF_GRAYED);
\r
7535 DrawMenuBar(hwndMain);
\r
7541 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7545 SetTrainingModeOn()
\r
7548 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7549 for (i = 0; i < N_BUTTONS; i++) {
\r
7550 if (buttonDesc[i].hwnd != NULL)
\r
7551 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7556 VOID SetTrainingModeOff()
\r
7559 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7560 for (i = 0; i < N_BUTTONS; i++) {
\r
7561 if (buttonDesc[i].hwnd != NULL)
\r
7562 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7568 SetUserThinkingEnables()
\r
7570 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7574 SetMachineThinkingEnables()
\r
7576 HMENU hMenu = GetMenu(hwndMain);
\r
7577 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7579 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7581 if (gameMode == MachinePlaysBlack) {
\r
7582 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7583 } else if (gameMode == MachinePlaysWhite) {
\r
7584 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7585 } else if (gameMode == TwoMachinesPlay) {
\r
7586 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7592 DisplayTitle(char *str)
\r
7594 char title[MSG_SIZ], *host;
\r
7595 if (str[0] != NULLCHAR) {
\r
7596 strcpy(title, str);
\r
7597 } else if (appData.icsActive) {
\r
7598 if (appData.icsCommPort[0] != NULLCHAR)
\r
7601 host = appData.icsHost;
\r
7602 sprintf(title, "%s: %s", szTitle, host);
\r
7603 } else if (appData.noChessProgram) {
\r
7604 strcpy(title, szTitle);
\r
7606 strcpy(title, szTitle);
\r
7607 strcat(title, ": ");
\r
7608 strcat(title, first.tidy);
\r
7610 SetWindowText(hwndMain, title);
\r
7615 DisplayMessage(char *str1, char *str2)
\r
7619 int remain = MESSAGE_TEXT_MAX - 1;
\r
7622 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7623 messageText[0] = NULLCHAR;
\r
7625 len = strlen(str1);
\r
7626 if (len > remain) len = remain;
\r
7627 strncpy(messageText, str1, len);
\r
7628 messageText[len] = NULLCHAR;
\r
7631 if (*str2 && remain >= 2) {
\r
7633 strcat(messageText, " ");
\r
7636 len = strlen(str2);
\r
7637 if (len > remain) len = remain;
\r
7638 strncat(messageText, str2, len);
\r
7640 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7642 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7646 hdc = GetDC(hwndMain);
\r
7647 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7648 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7649 &messageRect, messageText, strlen(messageText), NULL);
\r
7650 (void) SelectObject(hdc, oldFont);
\r
7651 (void) ReleaseDC(hwndMain, hdc);
\r
7655 DisplayError(char *str, int error)
\r
7657 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7663 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7664 NULL, error, LANG_NEUTRAL,
\r
7665 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7667 sprintf(buf, "%s:\n%s", str, buf2);
\r
7669 ErrorMap *em = errmap;
\r
7670 while (em->err != 0 && em->err != error) em++;
\r
7671 if (em->err != 0) {
\r
7672 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7674 sprintf(buf, "%s:\nError code %d", str, error);
\r
7679 ErrorPopUp("Error", buf);
\r
7684 DisplayMoveError(char *str)
\r
7686 fromX = fromY = -1;
\r
7687 ClearHighlights();
\r
7688 DrawPosition(FALSE, NULL);
\r
7689 if (appData.popupMoveErrors) {
\r
7690 ErrorPopUp("Error", str);
\r
7692 DisplayMessage(str, "");
\r
7693 moveErrorMessageUp = TRUE;
\r
7698 DisplayFatalError(char *str, int error, int exitStatus)
\r
7700 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7702 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7705 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7706 NULL, error, LANG_NEUTRAL,
\r
7707 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7709 sprintf(buf, "%s:\n%s", str, buf2);
\r
7711 ErrorMap *em = errmap;
\r
7712 while (em->err != 0 && em->err != error) em++;
\r
7713 if (em->err != 0) {
\r
7714 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7716 sprintf(buf, "%s:\nError code %d", str, error);
\r
7721 if (appData.debugMode) {
\r
7722 fprintf(debugFP, "%s: %s\n", label, str);
\r
7724 if (appData.popupExitMessage) {
\r
7725 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7726 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7728 ExitEvent(exitStatus);
\r
7733 DisplayInformation(char *str)
\r
7735 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7740 DisplayNote(char *str)
\r
7742 ErrorPopUp("Note", str);
\r
7747 char *title, *question, *replyPrefix;
\r
7752 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7754 static QuestionParams *qp;
\r
7755 char reply[MSG_SIZ];
\r
7758 switch (message) {
\r
7759 case WM_INITDIALOG:
\r
7760 qp = (QuestionParams *) lParam;
\r
7761 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7762 SetWindowText(hDlg, qp->title);
\r
7763 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7764 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7768 switch (LOWORD(wParam)) {
\r
7770 strcpy(reply, qp->replyPrefix);
\r
7771 if (*reply) strcat(reply, " ");
\r
7772 len = strlen(reply);
\r
7773 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7774 strcat(reply, "\n");
\r
7775 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7776 EndDialog(hDlg, TRUE);
\r
7777 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7780 EndDialog(hDlg, FALSE);
\r
7791 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7793 QuestionParams qp;
\r
7797 qp.question = question;
\r
7798 qp.replyPrefix = replyPrefix;
\r
7800 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7801 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7802 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7803 FreeProcInstance(lpProc);
\r
7806 /* [AS] Pick FRC position */
\r
7807 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7809 static int * lpIndexFRC;
\r
7815 case WM_INITDIALOG:
\r
7816 lpIndexFRC = (int *) lParam;
\r
7818 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7820 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7821 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7822 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7823 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7828 switch( LOWORD(wParam) ) {
\r
7830 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7831 EndDialog( hDlg, 0 );
\r
7832 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7835 EndDialog( hDlg, 1 );
\r
7837 case IDC_NFG_Edit:
\r
7838 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7839 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7841 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7844 case IDC_NFG_Random:
\r
7845 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7846 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7859 int index = appData.defaultFrcPosition;
\r
7860 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7862 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7864 if( result == 0 ) {
\r
7865 appData.defaultFrcPosition = index;
\r
7871 /* [AS] Game list options */
\r
7877 static GLT_Item GLT_ItemInfo[] = {
\r
7878 { GLT_EVENT, "Event" },
\r
7879 { GLT_SITE, "Site" },
\r
7880 { GLT_DATE, "Date" },
\r
7881 { GLT_ROUND, "Round" },
\r
7882 { GLT_PLAYERS, "Players" },
\r
7883 { GLT_RESULT, "Result" },
\r
7884 { GLT_WHITE_ELO, "White Rating" },
\r
7885 { GLT_BLACK_ELO, "Black Rating" },
\r
7886 { GLT_TIME_CONTROL,"Time Control" },
\r
7887 { GLT_VARIANT, "Variant" },
\r
7888 { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },
\r
7889 { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom
\r
7893 const char * GLT_FindItem( char id )
\r
7895 const char * result = 0;
\r
7897 GLT_Item * list = GLT_ItemInfo;
\r
7899 while( list->id != 0 ) {
\r
7900 if( list->id == id ) {
\r
7901 result = list->name;
\r
7911 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )
\r
7913 const char * name = GLT_FindItem( id );
\r
7916 if( index >= 0 ) {
\r
7917 SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );
\r
7920 SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );
\r
7925 void GLT_TagsToList( HWND hDlg, char * tags )
\r
7929 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7932 GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
\r
7936 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );
\r
7938 pc = GLT_ALL_TAGS;
\r
7941 if( strchr( tags, *pc ) == 0 ) {
\r
7942 GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
\r
7947 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7950 char GLT_ListItemToTag( HWND hDlg, int index )
\r
7952 char result = '\0';
\r
7955 GLT_Item * list = GLT_ItemInfo;
\r
7957 if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {
\r
7958 while( list->id != 0 ) {
\r
7959 if( strcmp( list->name, name ) == 0 ) {
\r
7960 result = list->id;
\r
7971 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7973 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7974 int idx2 = idx1 + delta;
\r
7975 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7977 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7980 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7981 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7982 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7983 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7987 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7989 static char glt[64];
\r
7990 static char * lpUserGLT;
\r
7994 case WM_INITDIALOG:
\r
7995 lpUserGLT = (char *) lParam;
\r
7997 strcpy( glt, lpUserGLT );
\r
7999 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8001 /* Initialize list */
\r
8002 GLT_TagsToList( hDlg, glt );
\r
8004 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8009 switch( LOWORD(wParam) ) {
\r
8012 char * pc = lpUserGLT;
\r
8014 // int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8018 id = GLT_ListItemToTag( hDlg, idx );
\r
8022 } while( id != '\0' );
\r
8024 EndDialog( hDlg, 0 );
\r
8027 EndDialog( hDlg, 1 );
\r
8030 case IDC_GLT_Default:
\r
8031 strcpy( glt, GLT_DEFAULT_TAGS );
\r
8032 GLT_TagsToList( hDlg, glt );
\r
8035 case IDC_GLT_Restore:
\r
8036 strcpy( glt, lpUserGLT );
\r
8037 GLT_TagsToList( hDlg, glt );
\r
8041 GLT_MoveSelection( hDlg, -1 );
\r
8044 case IDC_GLT_Down:
\r
8045 GLT_MoveSelection( hDlg, +1 );
\r
8055 int GameListOptions()
\r
8059 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8061 strcpy( glt, appData.gameListTags );
\r
8063 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );
\r
8065 if( result == 0 ) {
\r
8066 /* [AS] Memory leak here! */
\r
8067 appData.gameListTags = strdup( glt );
\r
8075 DisplayIcsInteractionTitle(char *str)
\r
8077 char consoleTitle[MSG_SIZ];
\r
8079 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8080 SetWindowText(hwndConsole, consoleTitle);
\r
8084 DrawPosition(int fullRedraw, Board board)
\r
8086 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8089 void NotifyFrontendLogin()
\r
8092 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8098 fromX = fromY = -1;
\r
8099 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8100 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8101 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8102 dragInfo.lastpos = dragInfo.pos;
\r
8103 dragInfo.start.x = dragInfo.start.y = -1;
\r
8104 dragInfo.from = dragInfo.start;
\r
8106 DrawPosition(TRUE, NULL);
\r
8112 CommentPopUp(char *title, char *str)
\r
8114 HWND hwnd = GetActiveWindow();
\r
8115 EitherCommentPopUp(0, title, str, FALSE);
\r
8117 SetActiveWindow(hwnd);
\r
8121 CommentPopDown(void)
\r
8123 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8124 if (commentDialog) {
\r
8125 ShowWindow(commentDialog, SW_HIDE);
\r
8127 commentUp = FALSE;
\r
8131 EditCommentPopUp(int index, char *title, char *str)
\r
8133 EitherCommentPopUp(index, title, str, TRUE);
\r
8140 MyPlaySound(&sounds[(int)SoundMove]);
\r
8143 VOID PlayIcsWinSound()
\r
8145 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8148 VOID PlayIcsLossSound()
\r
8150 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8153 VOID PlayIcsDrawSound()
\r
8155 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8158 VOID PlayIcsUnfinishedSound()
\r
8160 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8166 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8174 consoleEcho = TRUE;
\r
8175 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8176 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8177 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8186 consoleEcho = FALSE;
\r
8187 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8188 /* This works OK: set text and background both to the same color */
\r
8190 cf.crTextColor = COLOR_ECHOOFF;
\r
8191 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8192 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8195 /* No Raw()...? */
\r
8197 void Colorize(ColorClass cc, int continuation)
\r
8199 currentColorClass = cc;
\r
8200 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8201 consoleCF.crTextColor = textAttribs[cc].color;
\r
8202 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8203 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8209 static char buf[MSG_SIZ];
\r
8210 DWORD bufsiz = MSG_SIZ;
\r
8212 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8213 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8215 if (!GetUserName(buf, &bufsiz)) {
\r
8216 /*DisplayError("Error getting user name", GetLastError());*/
\r
8217 strcpy(buf, "User");
\r
8225 static char buf[MSG_SIZ];
\r
8226 DWORD bufsiz = MSG_SIZ;
\r
8228 if (!GetComputerName(buf, &bufsiz)) {
\r
8229 /*DisplayError("Error getting host name", GetLastError());*/
\r
8230 strcpy(buf, "Unknown");
\r
8237 ClockTimerRunning()
\r
8239 return clockTimerEvent != 0;
\r
8245 if (clockTimerEvent == 0) return FALSE;
\r
8246 KillTimer(hwndMain, clockTimerEvent);
\r
8247 clockTimerEvent = 0;
\r
8252 StartClockTimer(long millisec)
\r
8254 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8255 (UINT) millisec, NULL);
\r
8259 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8262 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8264 if(appData.noGUI) return;
\r
8265 hdc = GetDC(hwndMain);
\r
8266 if (!IsIconic(hwndMain)) {
\r
8267 DisplayAClock(hdc, timeRemaining, highlight,
\r
8268 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8270 if (highlight && iconCurrent == iconBlack) {
\r
8271 iconCurrent = iconWhite;
\r
8272 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8273 if (IsIconic(hwndMain)) {
\r
8274 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8277 (void) ReleaseDC(hwndMain, hdc);
\r
8279 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8283 DisplayBlackClock(long timeRemaining, int highlight)
\r
8286 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8288 if(appData.noGUI) return;
\r
8289 hdc = GetDC(hwndMain);
\r
8290 if (!IsIconic(hwndMain)) {
\r
8291 DisplayAClock(hdc, timeRemaining, highlight,
\r
8292 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8294 if (highlight && iconCurrent == iconWhite) {
\r
8295 iconCurrent = iconBlack;
\r
8296 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8297 if (IsIconic(hwndMain)) {
\r
8298 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8301 (void) ReleaseDC(hwndMain, hdc);
\r
8303 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8308 LoadGameTimerRunning()
\r
8310 return loadGameTimerEvent != 0;
\r
8314 StopLoadGameTimer()
\r
8316 if (loadGameTimerEvent == 0) return FALSE;
\r
8317 KillTimer(hwndMain, loadGameTimerEvent);
\r
8318 loadGameTimerEvent = 0;
\r
8323 StartLoadGameTimer(long millisec)
\r
8325 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8326 (UINT) millisec, NULL);
\r
8334 char fileTitle[MSG_SIZ];
\r
8336 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8337 f = OpenFileDialog(hwndMain, "a", defName,
\r
8338 appData.oldSaveStyle ? "gam" : "pgn",
\r
8340 "Save Game to File", NULL, fileTitle, NULL);
\r
8342 SaveGame(f, 0, "");
\r
8349 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8351 if (delayedTimerEvent != 0) {
\r
8352 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8353 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8355 KillTimer(hwndMain, delayedTimerEvent);
\r
8356 delayedTimerEvent = 0;
\r
8357 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8358 delayedTimerCallback();
\r
8360 delayedTimerCallback = cb;
\r
8361 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8362 (UINT) millisec, NULL);
\r
8365 DelayedEventCallback
\r
8368 if (delayedTimerEvent) {
\r
8369 return delayedTimerCallback;
\r
8376 CancelDelayedEvent()
\r
8378 if (delayedTimerEvent) {
\r
8379 KillTimer(hwndMain, delayedTimerEvent);
\r
8380 delayedTimerEvent = 0;
\r
8384 DWORD GetWin32Priority(int nice)
\r
8385 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8387 REALTIME_PRIORITY_CLASS 0x00000100
\r
8388 HIGH_PRIORITY_CLASS 0x00000080
\r
8389 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8390 NORMAL_PRIORITY_CLASS 0x00000020
\r
8391 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8392 IDLE_PRIORITY_CLASS 0x00000040
\r
8394 if (nice < -15) return 0x00000080;
\r
8395 if (nice < 0) return 0x00008000;
\r
8396 if (nice == 0) return 0x00000020;
\r
8397 if (nice < 15) return 0x00004000;
\r
8398 return 0x00000040;
\r
8401 /* Start a child process running the given program.
\r
8402 The process's standard output can be read from "from", and its
\r
8403 standard input can be written to "to".
\r
8404 Exit with fatal error if anything goes wrong.
\r
8405 Returns an opaque pointer that can be used to destroy the process
\r
8409 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8411 #define BUFSIZE 4096
\r
8413 HANDLE hChildStdinRd, hChildStdinWr,
\r
8414 hChildStdoutRd, hChildStdoutWr;
\r
8415 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8416 SECURITY_ATTRIBUTES saAttr;
\r
8418 PROCESS_INFORMATION piProcInfo;
\r
8419 STARTUPINFO siStartInfo;
\r
8421 char buf[MSG_SIZ];
\r
8424 if (appData.debugMode) {
\r
8425 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8430 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8431 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8432 saAttr.bInheritHandle = TRUE;
\r
8433 saAttr.lpSecurityDescriptor = NULL;
\r
8436 * The steps for redirecting child's STDOUT:
\r
8437 * 1. Create anonymous pipe to be STDOUT for child.
\r
8438 * 2. Create a noninheritable duplicate of read handle,
\r
8439 * and close the inheritable read handle.
\r
8442 /* Create a pipe for the child's STDOUT. */
\r
8443 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8444 return GetLastError();
\r
8447 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8448 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8449 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8450 FALSE, /* not inherited */
\r
8451 DUPLICATE_SAME_ACCESS);
\r
8453 return GetLastError();
\r
8455 CloseHandle(hChildStdoutRd);
\r
8458 * The steps for redirecting child's STDIN:
\r
8459 * 1. Create anonymous pipe to be STDIN for child.
\r
8460 * 2. Create a noninheritable duplicate of write handle,
\r
8461 * and close the inheritable write handle.
\r
8464 /* Create a pipe for the child's STDIN. */
\r
8465 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8466 return GetLastError();
\r
8469 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8470 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8471 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8472 FALSE, /* not inherited */
\r
8473 DUPLICATE_SAME_ACCESS);
\r
8475 return GetLastError();
\r
8477 CloseHandle(hChildStdinWr);
\r
8479 /* Arrange to (1) look in dir for the child .exe file, and
\r
8480 * (2) have dir be the child's working directory. Interpret
\r
8481 * dir relative to the directory WinBoard loaded from. */
\r
8482 GetCurrentDirectory(MSG_SIZ, buf);
\r
8483 SetCurrentDirectory(installDir);
\r
8484 SetCurrentDirectory(dir);
\r
8486 /* Now create the child process. */
\r
8488 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8489 siStartInfo.lpReserved = NULL;
\r
8490 siStartInfo.lpDesktop = NULL;
\r
8491 siStartInfo.lpTitle = NULL;
\r
8492 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8493 siStartInfo.cbReserved2 = 0;
\r
8494 siStartInfo.lpReserved2 = NULL;
\r
8495 siStartInfo.hStdInput = hChildStdinRd;
\r
8496 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8497 siStartInfo.hStdError = hChildStdoutWr;
\r
8499 fSuccess = CreateProcess(NULL,
\r
8500 cmdLine, /* command line */
\r
8501 NULL, /* process security attributes */
\r
8502 NULL, /* primary thread security attrs */
\r
8503 TRUE, /* handles are inherited */
\r
8504 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8505 NULL, /* use parent's environment */
\r
8507 &siStartInfo, /* STARTUPINFO pointer */
\r
8508 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8510 err = GetLastError();
\r
8511 SetCurrentDirectory(buf); /* return to prev directory */
\r
8516 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8517 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8518 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8521 /* Close the handles we don't need in the parent */
\r
8522 CloseHandle(piProcInfo.hThread);
\r
8523 CloseHandle(hChildStdinRd);
\r
8524 CloseHandle(hChildStdoutWr);
\r
8526 /* Prepare return value */
\r
8527 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8528 cp->kind = CPReal;
\r
8529 cp->hProcess = piProcInfo.hProcess;
\r
8530 cp->pid = piProcInfo.dwProcessId;
\r
8531 cp->hFrom = hChildStdoutRdDup;
\r
8532 cp->hTo = hChildStdinWrDup;
\r
8534 *pr = (void *) cp;
\r
8536 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8537 2000 where engines sometimes don't see the initial command(s)
\r
8538 from WinBoard and hang. I don't understand how that can happen,
\r
8539 but the Sleep is harmless, so I've put it in. Others have also
\r
8540 reported what may be the same problem, so hopefully this will fix
\r
8541 it for them too. */
\r
8549 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8551 ChildProc *cp; int result;
\r
8553 cp = (ChildProc *) pr;
\r
8554 if (cp == NULL) return;
\r
8556 switch (cp->kind) {
\r
8558 /* TerminateProcess is considered harmful, so... */
\r
8559 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8560 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8561 /* The following doesn't work because the chess program
\r
8562 doesn't "have the same console" as WinBoard. Maybe
\r
8563 we could arrange for this even though neither WinBoard
\r
8564 nor the chess program uses a console for stdio? */
\r
8565 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8567 /* [AS] Special termination modes for misbehaving programs... */
\r
8568 if( signal == 9 ) {
\r
8569 result = TerminateProcess( cp->hProcess, 0 );
\r
8571 if ( appData.debugMode) {
\r
8572 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8575 else if( signal == 10 ) {
\r
8576 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8578 if( dw != WAIT_OBJECT_0 ) {
\r
8579 result = TerminateProcess( cp->hProcess, 0 );
\r
8581 if ( appData.debugMode) {
\r
8582 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8588 CloseHandle(cp->hProcess);
\r
8592 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8596 closesocket(cp->sock);
\r
8601 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8602 closesocket(cp->sock);
\r
8603 closesocket(cp->sock2);
\r
8611 InterruptChildProcess(ProcRef pr)
\r
8615 cp = (ChildProc *) pr;
\r
8616 if (cp == NULL) return;
\r
8617 switch (cp->kind) {
\r
8619 /* The following doesn't work because the chess program
\r
8620 doesn't "have the same console" as WinBoard. Maybe
\r
8621 we could arrange for this even though neither WinBoard
\r
8622 nor the chess program uses a console for stdio */
\r
8623 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8628 /* Can't interrupt */
\r
8632 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8639 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8641 char cmdLine[MSG_SIZ];
\r
8643 if (port[0] == NULLCHAR) {
\r
8644 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8646 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8648 return StartChildProcess(cmdLine, "", pr);
\r
8652 /* Code to open TCP sockets */
\r
8655 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8660 struct sockaddr_in sa, mysa;
\r
8661 struct hostent FAR *hp;
\r
8662 unsigned short uport;
\r
8663 WORD wVersionRequested;
\r
8666 /* Initialize socket DLL */
\r
8667 wVersionRequested = MAKEWORD(1, 1);
\r
8668 err = WSAStartup(wVersionRequested, &wsaData);
\r
8669 if (err != 0) return err;
\r
8672 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8673 err = WSAGetLastError();
\r
8678 /* Bind local address using (mostly) don't-care values.
\r
8680 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8681 mysa.sin_family = AF_INET;
\r
8682 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8683 uport = (unsigned short) 0;
\r
8684 mysa.sin_port = htons(uport);
\r
8685 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8686 == SOCKET_ERROR) {
\r
8687 err = WSAGetLastError();
\r
8692 /* Resolve remote host name */
\r
8693 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8694 if (!(hp = gethostbyname(host))) {
\r
8695 unsigned int b0, b1, b2, b3;
\r
8697 err = WSAGetLastError();
\r
8699 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8700 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8701 hp->h_addrtype = AF_INET;
\r
8703 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8704 hp->h_addr_list[0] = (char *) malloc(4);
\r
8705 hp->h_addr_list[0][0] = (char) b0;
\r
8706 hp->h_addr_list[0][1] = (char) b1;
\r
8707 hp->h_addr_list[0][2] = (char) b2;
\r
8708 hp->h_addr_list[0][3] = (char) b3;
\r
8714 sa.sin_family = hp->h_addrtype;
\r
8715 uport = (unsigned short) atoi(port);
\r
8716 sa.sin_port = htons(uport);
\r
8717 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8719 /* Make connection */
\r
8720 if (connect(s, (struct sockaddr *) &sa,
\r
8721 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8722 err = WSAGetLastError();
\r
8727 /* Prepare return value */
\r
8728 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8729 cp->kind = CPSock;
\r
8731 *pr = (ProcRef *) cp;
\r
8737 OpenCommPort(char *name, ProcRef *pr)
\r
8742 char fullname[MSG_SIZ];
\r
8744 if (*name != '\\')
\r
8745 sprintf(fullname, "\\\\.\\%s", name);
\r
8747 strcpy(fullname, name);
\r
8749 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8750 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8751 if (h == (HANDLE) -1) {
\r
8752 return GetLastError();
\r
8756 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8758 /* Accumulate characters until a 100ms pause, then parse */
\r
8759 ct.ReadIntervalTimeout = 100;
\r
8760 ct.ReadTotalTimeoutMultiplier = 0;
\r
8761 ct.ReadTotalTimeoutConstant = 0;
\r
8762 ct.WriteTotalTimeoutMultiplier = 0;
\r
8763 ct.WriteTotalTimeoutConstant = 0;
\r
8764 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8766 /* Prepare return value */
\r
8767 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8768 cp->kind = CPComm;
\r
8771 *pr = (ProcRef *) cp;
\r
8777 OpenLoopback(ProcRef *pr)
\r
8779 DisplayFatalError("Not implemented", 0, 1);
\r
8785 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8790 struct sockaddr_in sa, mysa;
\r
8791 struct hostent FAR *hp;
\r
8792 unsigned short uport;
\r
8793 WORD wVersionRequested;
\r
8796 char stderrPortStr[MSG_SIZ];
\r
8798 /* Initialize socket DLL */
\r
8799 wVersionRequested = MAKEWORD(1, 1);
\r
8800 err = WSAStartup(wVersionRequested, &wsaData);
\r
8801 if (err != 0) return err;
\r
8803 /* Resolve remote host name */
\r
8804 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8805 if (!(hp = gethostbyname(host))) {
\r
8806 unsigned int b0, b1, b2, b3;
\r
8808 err = WSAGetLastError();
\r
8810 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8811 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8812 hp->h_addrtype = AF_INET;
\r
8814 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8815 hp->h_addr_list[0] = (char *) malloc(4);
\r
8816 hp->h_addr_list[0][0] = (char) b0;
\r
8817 hp->h_addr_list[0][1] = (char) b1;
\r
8818 hp->h_addr_list[0][2] = (char) b2;
\r
8819 hp->h_addr_list[0][3] = (char) b3;
\r
8825 sa.sin_family = hp->h_addrtype;
\r
8826 uport = (unsigned short) 514;
\r
8827 sa.sin_port = htons(uport);
\r
8828 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8830 /* Bind local socket to unused "privileged" port address
\r
8832 s = INVALID_SOCKET;
\r
8833 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8834 mysa.sin_family = AF_INET;
\r
8835 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8836 for (fromPort = 1023;; fromPort--) {
\r
8837 if (fromPort < 0) {
\r
8839 return WSAEADDRINUSE;
\r
8841 if (s == INVALID_SOCKET) {
\r
8842 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8843 err = WSAGetLastError();
\r
8848 uport = (unsigned short) fromPort;
\r
8849 mysa.sin_port = htons(uport);
\r
8850 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8851 == SOCKET_ERROR) {
\r
8852 err = WSAGetLastError();
\r
8853 if (err == WSAEADDRINUSE) continue;
\r
8857 if (connect(s, (struct sockaddr *) &sa,
\r
8858 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8859 err = WSAGetLastError();
\r
8860 if (err == WSAEADDRINUSE) {
\r
8871 /* Bind stderr local socket to unused "privileged" port address
\r
8873 s2 = INVALID_SOCKET;
\r
8874 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8875 mysa.sin_family = AF_INET;
\r
8876 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8877 for (fromPort = 1023;; fromPort--) {
\r
8878 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8879 if (fromPort < 0) {
\r
8880 (void) closesocket(s);
\r
8882 return WSAEADDRINUSE;
\r
8884 if (s2 == INVALID_SOCKET) {
\r
8885 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8886 err = WSAGetLastError();
\r
8892 uport = (unsigned short) fromPort;
\r
8893 mysa.sin_port = htons(uport);
\r
8894 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8895 == SOCKET_ERROR) {
\r
8896 err = WSAGetLastError();
\r
8897 if (err == WSAEADDRINUSE) continue;
\r
8898 (void) closesocket(s);
\r
8902 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8903 err = WSAGetLastError();
\r
8904 if (err == WSAEADDRINUSE) {
\r
8906 s2 = INVALID_SOCKET;
\r
8909 (void) closesocket(s);
\r
8910 (void) closesocket(s2);
\r
8916 prevStderrPort = fromPort; // remember port used
\r
8917 sprintf(stderrPortStr, "%d", fromPort);
\r
8919 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8920 err = WSAGetLastError();
\r
8921 (void) closesocket(s);
\r
8922 (void) closesocket(s2);
\r
8927 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8928 err = WSAGetLastError();
\r
8929 (void) closesocket(s);
\r
8930 (void) closesocket(s2);
\r
8934 if (*user == NULLCHAR) user = UserName();
\r
8935 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8936 err = WSAGetLastError();
\r
8937 (void) closesocket(s);
\r
8938 (void) closesocket(s2);
\r
8942 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8943 err = WSAGetLastError();
\r
8944 (void) closesocket(s);
\r
8945 (void) closesocket(s2);
\r
8950 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8951 err = WSAGetLastError();
\r
8952 (void) closesocket(s);
\r
8953 (void) closesocket(s2);
\r
8957 (void) closesocket(s2); /* Stop listening */
\r
8959 /* Prepare return value */
\r
8960 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8961 cp->kind = CPRcmd;
\r
8964 *pr = (ProcRef *) cp;
\r
8971 AddInputSource(ProcRef pr, int lineByLine,
\r
8972 InputCallback func, VOIDSTAR closure)
\r
8974 InputSource *is, *is2 = NULL;
\r
8975 ChildProc *cp = (ChildProc *) pr;
\r
8977 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8978 is->lineByLine = lineByLine;
\r
8980 is->closure = closure;
\r
8981 is->second = NULL;
\r
8982 is->next = is->buf;
\r
8983 if (pr == NoProc) {
\r
8984 is->kind = CPReal;
\r
8985 consoleInputSource = is;
\r
8987 is->kind = cp->kind;
\r
8989 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8990 we create all threads suspended so that the is->hThread variable can be
\r
8991 safely assigned, then let the threads start with ResumeThread.
\r
8993 switch (cp->kind) {
\r
8995 is->hFile = cp->hFrom;
\r
8996 cp->hFrom = NULL; /* now owned by InputThread */
\r
8998 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8999 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9003 is->hFile = cp->hFrom;
\r
9004 cp->hFrom = NULL; /* now owned by InputThread */
\r
9006 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9007 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9011 is->sock = cp->sock;
\r
9013 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9014 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9018 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9020 is->sock = cp->sock;
\r
9022 is2->sock = cp->sock2;
\r
9023 is2->second = is2;
\r
9025 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9026 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9028 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9029 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9033 if( is->hThread != NULL ) {
\r
9034 ResumeThread( is->hThread );
\r
9037 if( is2 != NULL && is2->hThread != NULL ) {
\r
9038 ResumeThread( is2->hThread );
\r
9042 return (InputSourceRef) is;
\r
9046 RemoveInputSource(InputSourceRef isr)
\r
9050 is = (InputSource *) isr;
\r
9051 is->hThread = NULL; /* tell thread to stop */
\r
9052 CloseHandle(is->hThread);
\r
9053 if (is->second != NULL) {
\r
9054 is->second->hThread = NULL;
\r
9055 CloseHandle(is->second->hThread);
\r
9059 int no_wrap(char *message, int count)
\r
9061 ConsoleOutput(message, count, FALSE);
\r
9066 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9069 int outCount = SOCKET_ERROR;
\r
9070 ChildProc *cp = (ChildProc *) pr;
\r
9071 static OVERLAPPED ovl;
\r
9072 static int line = 0;
\r
9076 if (appData.noJoin || !appData.useInternalWrap)
\r
9077 return no_wrap(message, count);
\r
9080 int width = get_term_width();
\r
9081 int len = wrap(NULL, message, count, width, &line);
\r
9082 char *msg = malloc(len);
\r
9086 return no_wrap(message, count);
\r
9089 dbgchk = wrap(msg, message, count, width, &line);
\r
9090 if (dbgchk != len && appData.debugMode)
\r
9091 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9092 ConsoleOutput(msg, len, FALSE);
\r
9099 if (ovl.hEvent == NULL) {
\r
9100 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9102 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9104 switch (cp->kind) {
\r
9107 outCount = send(cp->sock, message, count, 0);
\r
9108 if (outCount == SOCKET_ERROR) {
\r
9109 *outError = WSAGetLastError();
\r
9111 *outError = NO_ERROR;
\r
9116 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9117 &dOutCount, NULL)) {
\r
9118 *outError = NO_ERROR;
\r
9119 outCount = (int) dOutCount;
\r
9121 *outError = GetLastError();
\r
9126 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9127 &dOutCount, &ovl);
\r
9128 if (*outError == NO_ERROR) {
\r
9129 outCount = (int) dOutCount;
\r
9137 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9140 /* Ignore delay, not implemented for WinBoard */
\r
9141 return OutputToProcess(pr, message, count, outError);
\r
9146 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9147 char *buf, int count, int error)
\r
9149 DisplayFatalError("Not implemented", 0, 1);
\r
9152 /* see wgamelist.c for Game List functions */
\r
9153 /* see wedittags.c for Edit Tags functions */
\r
9160 char buf[MSG_SIZ];
\r
9163 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9164 f = fopen(buf, "r");
\r
9166 ProcessICSInitScript(f);
\r
9174 StartAnalysisClock()
\r
9176 if (analysisTimerEvent) return;
\r
9177 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9178 (UINT) 2000, NULL);
\r
9182 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9184 highlightInfo.sq[0].x = fromX;
\r
9185 highlightInfo.sq[0].y = fromY;
\r
9186 highlightInfo.sq[1].x = toX;
\r
9187 highlightInfo.sq[1].y = toY;
\r
9193 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9194 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9198 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9200 premoveHighlightInfo.sq[0].x = fromX;
\r
9201 premoveHighlightInfo.sq[0].y = fromY;
\r
9202 premoveHighlightInfo.sq[1].x = toX;
\r
9203 premoveHighlightInfo.sq[1].y = toY;
\r
9207 ClearPremoveHighlights()
\r
9209 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9210 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9214 ShutDownFrontEnd()
\r
9216 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9217 DeleteClipboardTempFiles();
\r
9223 if (IsIconic(hwndMain))
\r
9224 ShowWindow(hwndMain, SW_RESTORE);
\r
9226 SetActiveWindow(hwndMain);
\r
9230 * Prototypes for animation support routines
\r
9232 static void ScreenSquare(int column, int row, POINT * pt);
\r
9233 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9234 POINT frames[], int * nFrames);
\r
9238 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9239 { // [HGM] atomic: animate blast wave
\r
9241 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9242 explodeInfo.fromX = fromX;
\r
9243 explodeInfo.fromY = fromY;
\r
9244 explodeInfo.toX = toX;
\r
9245 explodeInfo.toY = toY;
\r
9246 for(i=1; i<nFrames; i++) {
\r
9247 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9248 DrawPosition(FALSE, NULL);
\r
9249 Sleep(appData.animSpeed);
\r
9251 explodeInfo.radius = 0;
\r
9252 DrawPosition(TRUE, NULL);
\r
9258 AnimateMove(board, fromX, fromY, toX, toY)
\r
9265 ChessSquare piece;
\r
9266 POINT start, finish, mid;
\r
9267 POINT frames[kFactor * 2 + 1];
\r
9270 if (!appData.animate) return;
\r
9271 if (doingSizing) return;
\r
9272 if (fromY < 0 || fromX < 0) return;
\r
9273 piece = board[fromY][fromX];
\r
9274 if (piece >= EmptySquare) return;
\r
9276 ScreenSquare(fromX, fromY, &start);
\r
9277 ScreenSquare(toX, toY, &finish);
\r
9279 /* All pieces except knights move in straight line */
\r
9280 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9281 mid.x = start.x + (finish.x - start.x) / 2;
\r
9282 mid.y = start.y + (finish.y - start.y) / 2;
\r
9284 /* Knight: make diagonal movement then straight */
\r
9285 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9286 mid.x = start.x + (finish.x - start.x) / 2;
\r
9290 mid.y = start.y + (finish.y - start.y) / 2;
\r
9294 /* Don't use as many frames for very short moves */
\r
9295 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9296 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9298 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9300 animInfo.from.x = fromX;
\r
9301 animInfo.from.y = fromY;
\r
9302 animInfo.to.x = toX;
\r
9303 animInfo.to.y = toY;
\r
9304 animInfo.lastpos = start;
\r
9305 animInfo.piece = piece;
\r
9306 for (n = 0; n < nFrames; n++) {
\r
9307 animInfo.pos = frames[n];
\r
9308 DrawPosition(FALSE, NULL);
\r
9309 animInfo.lastpos = animInfo.pos;
\r
9310 Sleep(appData.animSpeed);
\r
9312 animInfo.pos = finish;
\r
9313 DrawPosition(FALSE, NULL);
\r
9314 animInfo.piece = EmptySquare;
\r
9315 if(gameInfo.variant == VariantAtomic &&
\r
9316 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9317 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9320 /* Convert board position to corner of screen rect and color */
\r
9323 ScreenSquare(column, row, pt)
\r
9324 int column; int row; POINT * pt;
\r
9327 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9328 pt->y = lineGap + row * (squareSize + lineGap);
\r
9330 pt->x = lineGap + column * (squareSize + lineGap);
\r
9331 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9335 /* Generate a series of frame coords from start->mid->finish.
\r
9336 The movement rate doubles until the half way point is
\r
9337 reached, then halves back down to the final destination,
\r
9338 which gives a nice slow in/out effect. The algorithmn
\r
9339 may seem to generate too many intermediates for short
\r
9340 moves, but remember that the purpose is to attract the
\r
9341 viewers attention to the piece about to be moved and
\r
9342 then to where it ends up. Too few frames would be less
\r
9346 Tween(start, mid, finish, factor, frames, nFrames)
\r
9347 POINT * start; POINT * mid;
\r
9348 POINT * finish; int factor;
\r
9349 POINT frames[]; int * nFrames;
\r
9351 int n, fraction = 1, count = 0;
\r
9353 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9354 for (n = 0; n < factor; n++)
\r
9356 for (n = 0; n < factor; n++) {
\r
9357 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9358 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9360 fraction = fraction / 2;
\r
9364 frames[count] = *mid;
\r
9367 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9369 for (n = 0; n < factor; n++) {
\r
9370 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9371 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9373 fraction = fraction * 2;
\r
9379 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9381 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9383 EvalGraphSet( first, last, current, pvInfoList );
\r