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 EditPosition:
\r
3790 case IcsExamining:
\r
3791 if (x < 0 || y < 0) break;
\r
3794 if (message == WM_MBUTTONDOWN) {
\r
3795 buttonCount = 3; /* even if system didn't think so */
\r
3796 if (wParam & MK_SHIFT)
\r
3797 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3799 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3800 } else { /* message == WM_RBUTTONDOWN */
\r
3801 /* Just have one menu, on the right button. Windows users don't
\r
3802 think to try the middle one, and sometimes other software steals
\r
3803 it, or it doesn't really exist. */
\r
3804 if(gameInfo.variant != VariantShogi)
\r
3805 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3807 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3811 if(!appData.icsEngineAnalyze) break;
3812 case IcsPlayingWhite:
3813 case IcsPlayingBlack:
3814 if(!appData.zippyPlay) goto noZip;
3815 case MachinePlaysWhite:
3816 case MachinePlaysBlack:
3817 case TwoMachinesPlay:
3820 if (!appData.dropMenu) {
3821 SetCapture(hwndMain);
3822 LoadPV(pt.x - boardRect.left, pt.y - boardRect.top);
3825 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
3826 gameMode == AnalyzeFile || gameMode == IcsObserving) break;
3829 if (x < 0 || y < 0) break;
3830 if (!appData.dropMenu || appData.testLegality &&
3831 gameInfo.variant != VariantBughouse &&
3832 gameInfo.variant != VariantCrazyhouse) break;
3835 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3836 SetupDropMenu(hmenu);
\r
3837 MenuPopup(hwnd, pt, hmenu, -1);
\r
3848 /* Preprocess messages for buttons in main window */
\r
3850 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3852 int id = GetWindowLong(hwnd, GWL_ID);
\r
3855 for (i=0; i<N_BUTTONS; i++) {
\r
3856 if (buttonDesc[i].id == id) break;
\r
3858 if (i == N_BUTTONS) return 0;
\r
3859 switch (message) {
\r
3864 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3865 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3872 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3875 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3876 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3877 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3878 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3880 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3882 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3883 PopUpMoveDialog((char)wParam);
\r
3889 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3892 /* Process messages for Promotion dialog box */
\r
3894 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3898 switch (message) {
\r
3899 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3900 /* Center the dialog over the application window */
\r
3901 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3902 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3903 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3904 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3905 SW_SHOW : SW_HIDE);
\r
3906 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3907 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3908 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3909 PieceToChar(WhiteAngel) != '~') ||
\r
3910 (PieceToChar(BlackAngel) >= 'A' &&
\r
3911 PieceToChar(BlackAngel) != '~') ) ?
\r
3912 SW_SHOW : SW_HIDE);
\r
3913 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3914 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3915 PieceToChar(WhiteMarshall) != '~') ||
\r
3916 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3917 PieceToChar(BlackMarshall) != '~') ) ?
\r
3918 SW_SHOW : SW_HIDE);
\r
3919 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3920 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3921 gameInfo.variant != VariantShogi ?
\r
3922 SW_SHOW : SW_HIDE);
\r
3923 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3924 gameInfo.variant != VariantShogi ?
\r
3925 SW_SHOW : SW_HIDE);
\r
3926 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3927 gameInfo.variant == VariantShogi ?
\r
3928 SW_SHOW : SW_HIDE);
\r
3929 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3930 gameInfo.variant == VariantShogi ?
\r
3931 SW_SHOW : SW_HIDE);
\r
3932 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3933 gameInfo.variant == VariantSuper ?
\r
3934 SW_SHOW : SW_HIDE);
\r
3937 case WM_COMMAND: /* message: received a command */
\r
3938 switch (LOWORD(wParam)) {
\r
3940 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3941 ClearHighlights();
\r
3942 DrawPosition(FALSE, NULL);
\r
3945 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3948 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3951 promoChar = PieceToChar(BlackRook);
\r
3954 promoChar = PieceToChar(BlackBishop);
\r
3956 case PB_Chancellor:
\r
3957 promoChar = PieceToChar(BlackMarshall);
\r
3959 case PB_Archbishop:
\r
3960 promoChar = PieceToChar(BlackAngel);
\r
3963 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
3968 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3969 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
3970 only show the popup when we are already sure the move is valid or
\r
3971 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
3972 will figure out it is a promotion from the promoChar. */
\r
3973 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
3974 fromX = fromY = -1;
\r
3975 if (!appData.highlightLastMove) {
\r
3976 ClearHighlights();
\r
3977 DrawPosition(FALSE, NULL);
\r
3984 /* Pop up promotion dialog */
\r
3986 PromotionPopup(HWND hwnd)
\r
3990 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
3991 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
3992 hwnd, (DLGPROC)lpProc);
\r
3993 FreeProcInstance(lpProc);
\r
3999 DrawPosition(TRUE, NULL);
\r
4000 PromotionPopup(hwndMain);
\r
4003 /* Toggle ShowThinking */
\r
4005 ToggleShowThinking()
\r
4007 appData.showThinking = !appData.showThinking;
\r
4008 ShowThinkingEvent();
\r
4012 LoadGameDialog(HWND hwnd, char* title)
\r
4016 char fileTitle[MSG_SIZ];
\r
4017 f = OpenFileDialog(hwnd, "rb", "",
\r
4018 appData.oldSaveStyle ? "gam" : "pgn",
\r
4020 title, &number, fileTitle, NULL);
\r
4022 cmailMsgLoaded = FALSE;
\r
4023 if (number == 0) {
\r
4024 int error = GameListBuild(f);
\r
4026 DisplayError("Cannot build game list", error);
\r
4027 } else if (!ListEmpty(&gameList) &&
\r
4028 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4029 GameListPopUp(f, fileTitle);
\r
4032 GameListDestroy();
\r
4035 LoadGame(f, number, fileTitle, FALSE);
\r
4039 int get_term_width()
\r
4044 HFONT hfont, hold_font;
\r
4049 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4053 // get the text metrics
\r
4054 hdc = GetDC(hText);
\r
4055 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4056 if (consoleCF.dwEffects & CFE_BOLD)
\r
4057 lf.lfWeight = FW_BOLD;
\r
4058 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4059 lf.lfItalic = TRUE;
\r
4060 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4061 lf.lfStrikeOut = TRUE;
\r
4062 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4063 lf.lfUnderline = TRUE;
\r
4064 hfont = CreateFontIndirect(&lf);
\r
4065 hold_font = SelectObject(hdc, hfont);
\r
4066 GetTextMetrics(hdc, &tm);
\r
4067 SelectObject(hdc, hold_font);
\r
4068 DeleteObject(hfont);
\r
4069 ReleaseDC(hText, hdc);
\r
4071 // get the rectangle
\r
4072 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4074 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4077 void UpdateICSWidth(HWND hText)
\r
4079 LONG old_width, new_width;
\r
4081 new_width = get_term_width(hText, FALSE);
\r
4082 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4083 if (new_width != old_width)
\r
4085 ics_update_width(new_width);
\r
4086 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4091 ChangedConsoleFont()
\r
4094 CHARRANGE tmpsel, sel;
\r
4095 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4096 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4097 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4100 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4101 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4102 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4103 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4104 * size. This was undocumented in the version of MSVC++ that I had
\r
4105 * when I wrote the code, but is apparently documented now.
\r
4107 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4108 cfmt.bCharSet = f->lf.lfCharSet;
\r
4109 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4110 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4111 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4112 /* Why are the following seemingly needed too? */
\r
4113 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4114 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4115 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4117 tmpsel.cpMax = -1; /*999999?*/
\r
4118 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4119 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4120 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4121 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4123 paraf.cbSize = sizeof(paraf);
\r
4124 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4125 paraf.dxStartIndent = 0;
\r
4126 paraf.dxOffset = WRAP_INDENT;
\r
4127 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4128 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4129 UpdateICSWidth(hText);
\r
4132 /*---------------------------------------------------------------------------*\
\r
4134 * Window Proc for main window
\r
4136 \*---------------------------------------------------------------------------*/
\r
4138 /* Process messages for main window, etc. */
\r
4140 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4143 int wmId, wmEvent;
\r
4147 char fileTitle[MSG_SIZ];
\r
4148 char buf[MSG_SIZ];
\r
4149 static SnapData sd;
\r
4151 switch (message) {
\r
4153 case WM_PAINT: /* message: repaint portion of window */
\r
4157 case WM_ERASEBKGND:
\r
4158 if (IsIconic(hwnd)) {
\r
4159 /* Cheat; change the message */
\r
4160 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4162 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4166 case WM_LBUTTONDOWN:
\r
4167 case WM_MBUTTONDOWN:
\r
4168 case WM_RBUTTONDOWN:
\r
4169 case WM_LBUTTONUP:
\r
4170 case WM_MBUTTONUP:
\r
4171 case WM_RBUTTONUP:
\r
4172 case WM_MOUSEMOVE:
\r
4173 case WM_MOUSEWHEEL:
\r
4174 MouseEvent(hwnd, message, wParam, lParam);
\r
4177 JAWS_KB_NAVIGATION
\r
4181 JAWS_ALT_INTERCEPT
\r
4183 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4184 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4185 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4186 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4188 SendMessage(h, message, wParam, lParam);
\r
4189 } else if(lParam != KF_REPEAT) {
\r
4190 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4191 PopUpMoveDialog((char)wParam);
\r
4192 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4193 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4198 case WM_PALETTECHANGED:
\r
4199 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4201 HDC hdc = GetDC(hwndMain);
\r
4202 SelectPalette(hdc, hPal, TRUE);
\r
4203 nnew = RealizePalette(hdc);
\r
4205 paletteChanged = TRUE;
\r
4206 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4208 ReleaseDC(hwnd, hdc);
\r
4212 case WM_QUERYNEWPALETTE:
\r
4213 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4215 HDC hdc = GetDC(hwndMain);
\r
4216 paletteChanged = FALSE;
\r
4217 SelectPalette(hdc, hPal, FALSE);
\r
4218 nnew = RealizePalette(hdc);
\r
4220 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4222 ReleaseDC(hwnd, hdc);
\r
4227 case WM_COMMAND: /* message: command from application menu */
\r
4228 wmId = LOWORD(wParam);
\r
4229 wmEvent = HIWORD(wParam);
\r
4234 SAY("new game enter a move to play against the computer with white");
\r
4237 case IDM_NewGameFRC:
\r
4238 if( NewGameFRC() == 0 ) {
\r
4243 case IDM_NewVariant:
\r
4244 NewVariantPopup(hwnd);
\r
4247 case IDM_LoadGame:
\r
4248 LoadGameDialog(hwnd, "Load Game from File");
\r
4251 case IDM_LoadNextGame:
\r
4255 case IDM_LoadPrevGame:
\r
4259 case IDM_ReloadGame:
\r
4263 case IDM_LoadPosition:
\r
4264 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4265 Reset(FALSE, TRUE);
\r
4268 f = OpenFileDialog(hwnd, "rb", "",
\r
4269 appData.oldSaveStyle ? "pos" : "fen",
\r
4271 "Load Position from File", &number, fileTitle, NULL);
\r
4273 LoadPosition(f, number, fileTitle);
\r
4277 case IDM_LoadNextPosition:
\r
4278 ReloadPosition(1);
\r
4281 case IDM_LoadPrevPosition:
\r
4282 ReloadPosition(-1);
\r
4285 case IDM_ReloadPosition:
\r
4286 ReloadPosition(0);
\r
4289 case IDM_SaveGame:
\r
4290 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4291 f = OpenFileDialog(hwnd, "a", defName,
\r
4292 appData.oldSaveStyle ? "gam" : "pgn",
\r
4294 "Save Game to File", NULL, fileTitle, NULL);
\r
4296 SaveGame(f, 0, "");
\r
4300 case IDM_SavePosition:
\r
4301 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4302 f = OpenFileDialog(hwnd, "a", defName,
\r
4303 appData.oldSaveStyle ? "pos" : "fen",
\r
4305 "Save Position to File", NULL, fileTitle, NULL);
\r
4307 SavePosition(f, 0, "");
\r
4311 case IDM_SaveDiagram:
\r
4312 defName = "diagram";
\r
4313 f = OpenFileDialog(hwnd, "wb", defName,
\r
4316 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4322 case IDM_CopyGame:
\r
4323 CopyGameToClipboard();
\r
4326 case IDM_PasteGame:
\r
4327 PasteGameFromClipboard();
\r
4330 case IDM_CopyGameListToClipboard:
\r
4331 CopyGameListToClipboard();
\r
4334 /* [AS] Autodetect FEN or PGN data */
\r
4335 case IDM_PasteAny:
\r
4336 PasteGameOrFENFromClipboard();
\r
4339 /* [AS] Move history */
\r
4340 case IDM_ShowMoveHistory:
\r
4341 if( MoveHistoryIsUp() ) {
\r
4342 MoveHistoryPopDown();
\r
4345 MoveHistoryPopUp();
\r
4349 /* [AS] Eval graph */
\r
4350 case IDM_ShowEvalGraph:
\r
4351 if( EvalGraphIsUp() ) {
\r
4352 EvalGraphPopDown();
\r
4356 SetFocus(hwndMain);
\r
4360 /* [AS] Engine output */
\r
4361 case IDM_ShowEngineOutput:
\r
4362 if( EngineOutputIsUp() ) {
\r
4363 EngineOutputPopDown();
\r
4366 EngineOutputPopUp();
\r
4370 /* [AS] User adjudication */
\r
4371 case IDM_UserAdjudication_White:
\r
4372 UserAdjudicationEvent( +1 );
\r
4375 case IDM_UserAdjudication_Black:
\r
4376 UserAdjudicationEvent( -1 );
\r
4379 case IDM_UserAdjudication_Draw:
\r
4380 UserAdjudicationEvent( 0 );
\r
4383 /* [AS] Game list options dialog */
\r
4384 case IDM_GameListOptions:
\r
4385 GameListOptions();
\r
4392 case IDM_CopyPosition:
\r
4393 CopyFENToClipboard();
\r
4396 case IDM_PastePosition:
\r
4397 PasteFENFromClipboard();
\r
4400 case IDM_MailMove:
\r
4404 case IDM_ReloadCMailMsg:
\r
4405 Reset(TRUE, TRUE);
\r
4406 ReloadCmailMsgEvent(FALSE);
\r
4409 case IDM_Minimize:
\r
4410 ShowWindow(hwnd, SW_MINIMIZE);
\r
4417 case IDM_MachineWhite:
\r
4418 MachineWhiteEvent();
\r
4420 * refresh the tags dialog only if it's visible
\r
4422 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4424 tags = PGNTags(&gameInfo);
\r
4425 TagsPopUp(tags, CmailMsg());
\r
4428 SAY("computer starts playing white");
\r
4431 case IDM_MachineBlack:
\r
4432 MachineBlackEvent();
\r
4434 * refresh the tags dialog only if it's visible
\r
4436 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4438 tags = PGNTags(&gameInfo);
\r
4439 TagsPopUp(tags, CmailMsg());
\r
4442 SAY("computer starts playing black");
\r
4445 case IDM_TwoMachines:
\r
4446 TwoMachinesEvent();
\r
4448 * refresh the tags dialog only if it's visible
\r
4450 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4452 tags = PGNTags(&gameInfo);
\r
4453 TagsPopUp(tags, CmailMsg());
\r
4456 SAY("programs start playing each other");
\r
4459 case IDM_AnalysisMode:
\r
4460 if (!first.analysisSupport) {
\r
4461 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4462 DisplayError(buf, 0);
\r
4464 SAY("analyzing current position");
\r
4465 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4466 if (appData.icsActive) {
\r
4467 if (gameMode != IcsObserving) {
\r
4468 sprintf(buf, "You are not observing a game");
\r
4469 DisplayError(buf, 0);
\r
4470 /* secure check */
\r
4471 if (appData.icsEngineAnalyze) {
\r
4472 if (appData.debugMode)
\r
4473 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4474 ExitAnalyzeMode();
\r
4480 /* if enable, user want disable icsEngineAnalyze */
\r
4481 if (appData.icsEngineAnalyze) {
\r
4482 ExitAnalyzeMode();
\r
4486 appData.icsEngineAnalyze = TRUE;
\r
4487 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4490 if (!appData.showThinking) ToggleShowThinking();
\r
4491 AnalyzeModeEvent();
\r
4495 case IDM_AnalyzeFile:
\r
4496 if (!first.analysisSupport) {
\r
4497 char buf[MSG_SIZ];
\r
4498 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4499 DisplayError(buf, 0);
\r
4501 if (!appData.showThinking) ToggleShowThinking();
\r
4502 AnalyzeFileEvent();
\r
4503 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4504 AnalysisPeriodicEvent(1);
\r
4508 case IDM_IcsClient:
\r
4512 case IDM_EditGame:
\r
4517 case IDM_EditPosition:
\r
4518 EditPositionEvent();
\r
4519 SAY("to set up a position type a FEN");
\r
4522 case IDM_Training:
\r
4526 case IDM_ShowGameList:
\r
4527 ShowGameListProc();
\r
4530 case IDM_EditTags:
\r
4534 case IDM_EditComment:
\r
4535 if (commentUp && editComment) {
\r
4538 EditCommentEvent();
\r
4558 case IDM_CallFlag:
\r
4578 case IDM_StopObserving:
\r
4579 StopObservingEvent();
\r
4582 case IDM_StopExamining:
\r
4583 StopExaminingEvent();
\r
4586 case IDM_TypeInMove:
\r
4587 PopUpMoveDialog('\000');
\r
4590 case IDM_TypeInName:
\r
4591 PopUpNameDialog('\000');
\r
4594 case IDM_Backward:
\r
4596 SetFocus(hwndMain);
\r
4603 SetFocus(hwndMain);
\r
4608 SetFocus(hwndMain);
\r
4613 SetFocus(hwndMain);
\r
4620 case IDM_TruncateGame:
\r
4621 TruncateGameEvent();
\r
4628 case IDM_RetractMove:
\r
4629 RetractMoveEvent();
\r
4632 case IDM_FlipView:
\r
4633 flipView = !flipView;
\r
4634 DrawPosition(FALSE, NULL);
\r
4637 case IDM_FlipClock:
\r
4638 flipClock = !flipClock;
\r
4639 DisplayBothClocks();
\r
4640 DrawPosition(FALSE, NULL);
\r
4643 case IDM_MuteSounds:
\r
4644 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4645 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4646 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4649 case IDM_GeneralOptions:
\r
4650 GeneralOptionsPopup(hwnd);
\r
4651 DrawPosition(TRUE, NULL);
\r
4654 case IDM_BoardOptions:
\r
4655 BoardOptionsPopup(hwnd);
\r
4658 case IDM_EnginePlayOptions:
\r
4659 EnginePlayOptionsPopup(hwnd);
\r
4662 case IDM_Engine1Options:
\r
4663 EngineOptionsPopup(hwnd, &first);
\r
4666 case IDM_Engine2Options:
\r
4667 EngineOptionsPopup(hwnd, &second);
\r
4670 case IDM_OptionsUCI:
\r
4671 UciOptionsPopup(hwnd);
\r
4674 case IDM_IcsOptions:
\r
4675 IcsOptionsPopup(hwnd);
\r
4679 FontsOptionsPopup(hwnd);
\r
4683 SoundOptionsPopup(hwnd);
\r
4686 case IDM_CommPort:
\r
4687 CommPortOptionsPopup(hwnd);
\r
4690 case IDM_LoadOptions:
\r
4691 LoadOptionsPopup(hwnd);
\r
4694 case IDM_SaveOptions:
\r
4695 SaveOptionsPopup(hwnd);
\r
4698 case IDM_TimeControl:
\r
4699 TimeControlOptionsPopup(hwnd);
\r
4702 case IDM_SaveSettings:
\r
4703 SaveSettings(settingsFileName);
\r
4706 case IDM_SaveSettingsOnExit:
\r
4707 saveSettingsOnExit = !saveSettingsOnExit;
\r
4708 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4709 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4710 MF_CHECKED : MF_UNCHECKED));
\r
4721 case IDM_AboutGame:
\r
4726 appData.debugMode = !appData.debugMode;
\r
4727 if (appData.debugMode) {
\r
4728 char dir[MSG_SIZ];
\r
4729 GetCurrentDirectory(MSG_SIZ, dir);
\r
4730 SetCurrentDirectory(installDir);
\r
4731 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4732 SetCurrentDirectory(dir);
\r
4733 setbuf(debugFP, NULL);
\r
4740 case IDM_HELPCONTENTS:
\r
4741 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4742 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4743 MessageBox (GetFocus(),
\r
4744 "Unable to activate help",
\r
4745 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4749 case IDM_HELPSEARCH:
\r
4750 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4751 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4752 MessageBox (GetFocus(),
\r
4753 "Unable to activate help",
\r
4754 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4758 case IDM_HELPHELP:
\r
4759 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4760 MessageBox (GetFocus(),
\r
4761 "Unable to activate help",
\r
4762 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4767 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4769 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4770 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4771 FreeProcInstance(lpProc);
\r
4774 case IDM_DirectCommand1:
\r
4775 AskQuestionEvent("Direct Command",
\r
4776 "Send to chess program:", "", "1");
\r
4778 case IDM_DirectCommand2:
\r
4779 AskQuestionEvent("Direct Command",
\r
4780 "Send to second chess program:", "", "2");
\r
4783 case EP_WhitePawn:
\r
4784 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4785 fromX = fromY = -1;
\r
4788 case EP_WhiteKnight:
\r
4789 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4790 fromX = fromY = -1;
\r
4793 case EP_WhiteBishop:
\r
4794 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4795 fromX = fromY = -1;
\r
4798 case EP_WhiteRook:
\r
4799 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4800 fromX = fromY = -1;
\r
4803 case EP_WhiteQueen:
\r
4804 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4805 fromX = fromY = -1;
\r
4808 case EP_WhiteFerz:
\r
4809 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4810 fromX = fromY = -1;
\r
4813 case EP_WhiteWazir:
\r
4814 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4815 fromX = fromY = -1;
\r
4818 case EP_WhiteAlfil:
\r
4819 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4820 fromX = fromY = -1;
\r
4823 case EP_WhiteCannon:
\r
4824 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4825 fromX = fromY = -1;
\r
4828 case EP_WhiteCardinal:
\r
4829 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4830 fromX = fromY = -1;
\r
4833 case EP_WhiteMarshall:
\r
4834 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4835 fromX = fromY = -1;
\r
4838 case EP_WhiteKing:
\r
4839 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4840 fromX = fromY = -1;
\r
4843 case EP_BlackPawn:
\r
4844 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4845 fromX = fromY = -1;
\r
4848 case EP_BlackKnight:
\r
4849 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4850 fromX = fromY = -1;
\r
4853 case EP_BlackBishop:
\r
4854 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4855 fromX = fromY = -1;
\r
4858 case EP_BlackRook:
\r
4859 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4860 fromX = fromY = -1;
\r
4863 case EP_BlackQueen:
\r
4864 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4865 fromX = fromY = -1;
\r
4868 case EP_BlackFerz:
\r
4869 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4870 fromX = fromY = -1;
\r
4873 case EP_BlackWazir:
\r
4874 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4875 fromX = fromY = -1;
\r
4878 case EP_BlackAlfil:
\r
4879 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4880 fromX = fromY = -1;
\r
4883 case EP_BlackCannon:
\r
4884 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4885 fromX = fromY = -1;
\r
4888 case EP_BlackCardinal:
\r
4889 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4890 fromX = fromY = -1;
\r
4893 case EP_BlackMarshall:
\r
4894 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4895 fromX = fromY = -1;
\r
4898 case EP_BlackKing:
\r
4899 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4900 fromX = fromY = -1;
\r
4903 case EP_EmptySquare:
\r
4904 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4905 fromX = fromY = -1;
\r
4908 case EP_ClearBoard:
\r
4909 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4910 fromX = fromY = -1;
\r
4914 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4915 fromX = fromY = -1;
\r
4919 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4920 fromX = fromY = -1;
\r
4924 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4925 fromX = fromY = -1;
\r
4929 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4930 fromX = fromY = -1;
\r
4934 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4935 fromX = fromY = -1;
\r
4939 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4940 fromX = fromY = -1;
\r
4944 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4945 fromX = fromY = -1;
\r
4949 DropMenuEvent(WhiteRook, fromX, fromY);
\r
4950 fromX = fromY = -1;
\r
4954 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
4955 fromX = fromY = -1;
\r
4959 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4965 case CLOCK_TIMER_ID:
\r
4966 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
4967 clockTimerEvent = 0;
\r
4968 DecrementClocks(); /* call into back end */
\r
4970 case LOAD_GAME_TIMER_ID:
\r
4971 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
4972 loadGameTimerEvent = 0;
\r
4973 AutoPlayGameLoop(); /* call into back end */
\r
4975 case ANALYSIS_TIMER_ID:
\r
4976 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
4977 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
4978 AnalysisPeriodicEvent(0);
\r
4980 KillTimer(hwnd, analysisTimerEvent);
\r
4981 analysisTimerEvent = 0;
\r
4984 case DELAYED_TIMER_ID:
\r
4985 KillTimer(hwnd, delayedTimerEvent);
\r
4986 delayedTimerEvent = 0;
\r
4987 delayedTimerCallback();
\r
4992 case WM_USER_Input:
\r
4993 InputEvent(hwnd, message, wParam, lParam);
\r
4996 /* [AS] Also move "attached" child windows */
\r
4997 case WM_WINDOWPOSCHANGING:
\r
4999 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5000 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5002 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5003 /* Window is moving */
\r
5006 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5007 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5008 rcMain.right = wpMain.x + wpMain.width;
\r
5009 rcMain.top = wpMain.y;
\r
5010 rcMain.bottom = wpMain.y + wpMain.height;
\r
5012 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5013 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5014 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5015 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5016 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5017 wpMain.x = lpwp->x;
\r
5018 wpMain.y = lpwp->y;
\r
5023 /* [AS] Snapping */
\r
5024 case WM_ENTERSIZEMOVE:
\r
5025 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5026 if (hwnd == hwndMain) {
\r
5027 doingSizing = TRUE;
\r
5030 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5034 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5035 if (hwnd == hwndMain) {
\r
5036 lastSizing = wParam;
\r
5041 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5042 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5044 case WM_EXITSIZEMOVE:
\r
5045 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5046 if (hwnd == hwndMain) {
\r
5048 doingSizing = FALSE;
\r
5049 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5050 GetClientRect(hwnd, &client);
\r
5051 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5053 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5055 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5058 case WM_DESTROY: /* message: window being destroyed */
\r
5059 PostQuitMessage(0);
\r
5063 if (hwnd == hwndMain) {
\r
5068 default: /* Passes it on if unprocessed */
\r
5069 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5074 /*---------------------------------------------------------------------------*\
\r
5076 * Misc utility routines
\r
5078 \*---------------------------------------------------------------------------*/
\r
5081 * Decent random number generator, at least not as bad as Windows
\r
5082 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5084 unsigned int randstate;
\r
5089 randstate = randstate * 1664525 + 1013904223;
\r
5090 return (int) randstate & 0x7fffffff;
\r
5094 mysrandom(unsigned int seed)
\r
5101 * returns TRUE if user selects a different color, FALSE otherwise
\r
5105 ChangeColor(HWND hwnd, COLORREF *which)
\r
5107 static BOOL firstTime = TRUE;
\r
5108 static DWORD customColors[16];
\r
5110 COLORREF newcolor;
\r
5115 /* Make initial colors in use available as custom colors */
\r
5116 /* Should we put the compiled-in defaults here instead? */
\r
5118 customColors[i++] = lightSquareColor & 0xffffff;
\r
5119 customColors[i++] = darkSquareColor & 0xffffff;
\r
5120 customColors[i++] = whitePieceColor & 0xffffff;
\r
5121 customColors[i++] = blackPieceColor & 0xffffff;
\r
5122 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5123 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5125 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5126 customColors[i++] = textAttribs[ccl].color;
\r
5128 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5129 firstTime = FALSE;
\r
5132 cc.lStructSize = sizeof(cc);
\r
5133 cc.hwndOwner = hwnd;
\r
5134 cc.hInstance = NULL;
\r
5135 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5136 cc.lpCustColors = (LPDWORD) customColors;
\r
5137 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5139 if (!ChooseColor(&cc)) return FALSE;
\r
5141 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5142 if (newcolor == *which) return FALSE;
\r
5143 *which = newcolor;
\r
5147 InitDrawingColors();
\r
5148 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5153 MyLoadSound(MySound *ms)
\r
5159 if (ms->data) free(ms->data);
\r
5162 switch (ms->name[0]) {
\r
5168 /* System sound from Control Panel. Don't preload here. */
\r
5172 if (ms->name[1] == NULLCHAR) {
\r
5173 /* "!" alone = silence */
\r
5176 /* Builtin wave resource. Error if not found. */
\r
5177 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5178 if (h == NULL) break;
\r
5179 ms->data = (void *)LoadResource(hInst, h);
\r
5180 if (h == NULL) break;
\r
5185 /* .wav file. Error if not found. */
\r
5186 f = fopen(ms->name, "rb");
\r
5187 if (f == NULL) break;
\r
5188 if (fstat(fileno(f), &st) < 0) break;
\r
5189 ms->data = malloc(st.st_size);
\r
5190 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5196 char buf[MSG_SIZ];
\r
5197 sprintf(buf, "Error loading sound %s", ms->name);
\r
5198 DisplayError(buf, GetLastError());
\r
5204 MyPlaySound(MySound *ms)
\r
5206 BOOLEAN ok = FALSE;
\r
5208 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5209 switch (ms->name[0]) {
\r
5211 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5216 /* System sound from Control Panel (deprecated feature).
\r
5217 "$" alone or an unset sound name gets default beep (still in use). */
\r
5218 if (ms->name[1]) {
\r
5219 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5221 if (!ok) ok = MessageBeep(MB_OK);
\r
5224 /* Builtin wave resource, or "!" alone for silence */
\r
5225 if (ms->name[1]) {
\r
5226 if (ms->data == NULL) return FALSE;
\r
5227 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5233 /* .wav file. Error if not found. */
\r
5234 if (ms->data == NULL) return FALSE;
\r
5235 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5238 /* Don't print an error: this can happen innocently if the sound driver
\r
5239 is busy; for instance, if another instance of WinBoard is playing
\r
5240 a sound at about the same time. */
\r
5246 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5249 OPENFILENAME *ofn;
\r
5250 static UINT *number; /* gross that this is static */
\r
5252 switch (message) {
\r
5253 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5254 /* Center the dialog over the application window */
\r
5255 ofn = (OPENFILENAME *) lParam;
\r
5256 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5257 number = (UINT *) ofn->lCustData;
\r
5258 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5262 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5263 return FALSE; /* Allow for further processing */
\r
5266 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5267 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5269 return FALSE; /* Allow for further processing */
\r
5275 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5277 static UINT *number;
\r
5278 OPENFILENAME *ofname;
\r
5281 case WM_INITDIALOG:
\r
5282 ofname = (OPENFILENAME *)lParam;
\r
5283 number = (UINT *)(ofname->lCustData);
\r
5286 ofnot = (OFNOTIFY *)lParam;
\r
5287 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5288 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5297 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5298 char *nameFilt, char *dlgTitle, UINT *number,
\r
5299 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5301 OPENFILENAME openFileName;
\r
5302 char buf1[MSG_SIZ];
\r
5305 if (fileName == NULL) fileName = buf1;
\r
5306 if (defName == NULL) {
\r
5307 strcpy(fileName, "*.");
\r
5308 strcat(fileName, defExt);
\r
5310 strcpy(fileName, defName);
\r
5312 if (fileTitle) strcpy(fileTitle, "");
\r
5313 if (number) *number = 0;
\r
5315 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5316 openFileName.hwndOwner = hwnd;
\r
5317 openFileName.hInstance = (HANDLE) hInst;
\r
5318 openFileName.lpstrFilter = nameFilt;
\r
5319 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5320 openFileName.nMaxCustFilter = 0L;
\r
5321 openFileName.nFilterIndex = 1L;
\r
5322 openFileName.lpstrFile = fileName;
\r
5323 openFileName.nMaxFile = MSG_SIZ;
\r
5324 openFileName.lpstrFileTitle = fileTitle;
\r
5325 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5326 openFileName.lpstrInitialDir = NULL;
\r
5327 openFileName.lpstrTitle = dlgTitle;
\r
5328 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5329 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5330 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5331 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5332 openFileName.nFileOffset = 0;
\r
5333 openFileName.nFileExtension = 0;
\r
5334 openFileName.lpstrDefExt = defExt;
\r
5335 openFileName.lCustData = (LONG) number;
\r
5336 openFileName.lpfnHook = oldDialog ?
\r
5337 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5338 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5340 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5341 GetOpenFileName(&openFileName)) {
\r
5342 /* open the file */
\r
5343 f = fopen(openFileName.lpstrFile, write);
\r
5345 MessageBox(hwnd, "File open failed", NULL,
\r
5346 MB_OK|MB_ICONEXCLAMATION);
\r
5350 int err = CommDlgExtendedError();
\r
5351 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5360 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5362 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5365 * Get the first pop-up menu in the menu template. This is the
\r
5366 * menu that TrackPopupMenu displays.
\r
5368 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5370 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5373 * TrackPopup uses screen coordinates, so convert the
\r
5374 * coordinates of the mouse click to screen coordinates.
\r
5376 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5378 /* Draw and track the floating pop-up menu. */
\r
5379 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5380 pt.x, pt.y, 0, hwnd, NULL);
\r
5382 /* Destroy the menu.*/
\r
5383 DestroyMenu(hmenu);
\r
5388 int sizeX, sizeY, newSizeX, newSizeY;
\r
5390 } ResizeEditPlusButtonsClosure;
\r
5393 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5395 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5399 if (hChild == cl->hText) return TRUE;
\r
5400 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5401 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5402 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5403 ScreenToClient(cl->hDlg, &pt);
\r
5404 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5405 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5409 /* Resize a dialog that has a (rich) edit field filling most of
\r
5410 the top, with a row of buttons below */
\r
5412 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5415 int newTextHeight, newTextWidth;
\r
5416 ResizeEditPlusButtonsClosure cl;
\r
5418 /*if (IsIconic(hDlg)) return;*/
\r
5419 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5421 cl.hdwp = BeginDeferWindowPos(8);
\r
5423 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5424 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5425 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5426 if (newTextHeight < 0) {
\r
5427 newSizeY += -newTextHeight;
\r
5428 newTextHeight = 0;
\r
5430 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5431 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5437 cl.newSizeX = newSizeX;
\r
5438 cl.newSizeY = newSizeY;
\r
5439 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5441 EndDeferWindowPos(cl.hdwp);
\r
5444 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5446 RECT rChild, rParent;
\r
5447 int wChild, hChild, wParent, hParent;
\r
5448 int wScreen, hScreen, xNew, yNew;
\r
5451 /* Get the Height and Width of the child window */
\r
5452 GetWindowRect (hwndChild, &rChild);
\r
5453 wChild = rChild.right - rChild.left;
\r
5454 hChild = rChild.bottom - rChild.top;
\r
5456 /* Get the Height and Width of the parent window */
\r
5457 GetWindowRect (hwndParent, &rParent);
\r
5458 wParent = rParent.right - rParent.left;
\r
5459 hParent = rParent.bottom - rParent.top;
\r
5461 /* Get the display limits */
\r
5462 hdc = GetDC (hwndChild);
\r
5463 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5464 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5465 ReleaseDC(hwndChild, hdc);
\r
5467 /* Calculate new X position, then adjust for screen */
\r
5468 xNew = rParent.left + ((wParent - wChild) /2);
\r
5471 } else if ((xNew+wChild) > wScreen) {
\r
5472 xNew = wScreen - wChild;
\r
5475 /* Calculate new Y position, then adjust for screen */
\r
5477 yNew = rParent.top + ((hParent - hChild) /2);
\r
5480 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5485 } else if ((yNew+hChild) > hScreen) {
\r
5486 yNew = hScreen - hChild;
\r
5489 /* Set it, and return */
\r
5490 return SetWindowPos (hwndChild, NULL,
\r
5491 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5494 /* Center one window over another */
\r
5495 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5497 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5500 /*---------------------------------------------------------------------------*\
\r
5502 * Startup Dialog functions
\r
5504 \*---------------------------------------------------------------------------*/
\r
5506 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5508 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5510 while (*cd != NULL) {
\r
5511 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5517 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5519 char buf1[MAX_ARG_LEN];
\r
5522 if (str[0] == '@') {
\r
5523 FILE* f = fopen(str + 1, "r");
\r
5525 DisplayFatalError(str + 1, errno, 2);
\r
5528 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5530 buf1[len] = NULLCHAR;
\r
5534 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5537 char buf[MSG_SIZ];
\r
5538 char *end = strchr(str, '\n');
\r
5539 if (end == NULL) return;
\r
5540 memcpy(buf, str, end - str);
\r
5541 buf[end - str] = NULLCHAR;
\r
5542 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5548 SetStartupDialogEnables(HWND hDlg)
\r
5550 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5551 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5552 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5553 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5554 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5555 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5556 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5557 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5558 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5559 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5560 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5561 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5562 IsDlgButtonChecked(hDlg, OPT_View));
\r
5566 QuoteForFilename(char *filename)
\r
5568 int dquote, space;
\r
5569 dquote = strchr(filename, '"') != NULL;
\r
5570 space = strchr(filename, ' ') != NULL;
\r
5571 if (dquote || space) {
\r
5583 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5585 char buf[MSG_SIZ];
\r
5588 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5589 q = QuoteForFilename(nthcp);
\r
5590 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5591 if (*nthdir != NULLCHAR) {
\r
5592 q = QuoteForFilename(nthdir);
\r
5593 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5595 if (*nthcp == NULLCHAR) {
\r
5596 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5597 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5598 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5599 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5604 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5606 char buf[MSG_SIZ];
\r
5610 switch (message) {
\r
5611 case WM_INITDIALOG:
\r
5612 /* Center the dialog */
\r
5613 CenterWindow (hDlg, GetDesktopWindow());
\r
5614 /* Initialize the dialog items */
\r
5615 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5616 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5617 firstChessProgramNames);
\r
5618 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5619 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5620 secondChessProgramNames);
\r
5621 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5622 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5623 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5624 if (*appData.icsHelper != NULLCHAR) {
\r
5625 char *q = QuoteForFilename(appData.icsHelper);
\r
5626 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5628 if (*appData.icsHost == NULLCHAR) {
\r
5629 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5630 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5631 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5632 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5633 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5636 if (appData.icsActive) {
\r
5637 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5639 else if (appData.noChessProgram) {
\r
5640 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5643 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5646 SetStartupDialogEnables(hDlg);
\r
5650 switch (LOWORD(wParam)) {
\r
5652 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5653 strcpy(buf, "/fcp=");
\r
5654 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5656 ParseArgs(StringGet, &p);
\r
5657 strcpy(buf, "/scp=");
\r
5658 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5660 ParseArgs(StringGet, &p);
\r
5661 appData.noChessProgram = FALSE;
\r
5662 appData.icsActive = FALSE;
\r
5663 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5664 strcpy(buf, "/ics /icshost=");
\r
5665 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5667 ParseArgs(StringGet, &p);
\r
5668 if (appData.zippyPlay) {
\r
5669 strcpy(buf, "/fcp=");
\r
5670 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5672 ParseArgs(StringGet, &p);
\r
5674 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5675 appData.noChessProgram = TRUE;
\r
5676 appData.icsActive = FALSE;
\r
5678 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5679 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5682 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5683 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5685 ParseArgs(StringGet, &p);
\r
5687 EndDialog(hDlg, TRUE);
\r
5694 case IDM_HELPCONTENTS:
\r
5695 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5696 MessageBox (GetFocus(),
\r
5697 "Unable to activate help",
\r
5698 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5703 SetStartupDialogEnables(hDlg);
\r
5711 /*---------------------------------------------------------------------------*\
\r
5713 * About box dialog functions
\r
5715 \*---------------------------------------------------------------------------*/
\r
5717 /* Process messages for "About" dialog box */
\r
5719 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5721 switch (message) {
\r
5722 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5723 /* Center the dialog over the application window */
\r
5724 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5725 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5729 case WM_COMMAND: /* message: received a command */
\r
5730 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5731 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5732 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5740 /*---------------------------------------------------------------------------*\
\r
5742 * Comment Dialog functions
\r
5744 \*---------------------------------------------------------------------------*/
\r
5747 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5749 static HANDLE hwndText = NULL;
\r
5750 int len, newSizeX, newSizeY, flags;
\r
5751 static int sizeX, sizeY;
\r
5756 switch (message) {
\r
5757 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5758 /* Initialize the dialog items */
\r
5759 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5760 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5761 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5762 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5763 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5764 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5765 SetWindowText(hDlg, commentTitle);
\r
5766 if (editComment) {
\r
5767 SetFocus(hwndText);
\r
5769 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5771 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5772 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5773 MAKELPARAM(FALSE, 0));
\r
5774 /* Size and position the dialog */
\r
5775 if (!commentDialog) {
\r
5776 commentDialog = hDlg;
\r
5777 flags = SWP_NOZORDER;
\r
5778 GetClientRect(hDlg, &rect);
\r
5779 sizeX = rect.right;
\r
5780 sizeY = rect.bottom;
\r
5781 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5782 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5783 WINDOWPLACEMENT wp;
\r
5784 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5785 wp.length = sizeof(WINDOWPLACEMENT);
\r
5787 wp.showCmd = SW_SHOW;
\r
5788 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5789 wp.rcNormalPosition.left = wpComment.x;
\r
5790 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5791 wp.rcNormalPosition.top = wpComment.y;
\r
5792 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5793 SetWindowPlacement(hDlg, &wp);
\r
5795 GetClientRect(hDlg, &rect);
\r
5796 newSizeX = rect.right;
\r
5797 newSizeY = rect.bottom;
\r
5798 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5799 newSizeX, newSizeY);
\r
5806 case WM_COMMAND: /* message: received a command */
\r
5807 switch (LOWORD(wParam)) {
\r
5809 if (editComment) {
\r
5811 /* Read changed options from the dialog box */
\r
5812 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5813 len = GetWindowTextLength(hwndText);
\r
5814 str = (char *) malloc(len + 1);
\r
5815 GetWindowText(hwndText, str, len + 1);
\r
5824 ReplaceComment(commentIndex, str);
\r
5831 case OPT_CancelComment:
\r
5835 case OPT_ClearComment:
\r
5836 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5839 case OPT_EditComment:
\r
5840 EditCommentEvent();
\r
5849 newSizeX = LOWORD(lParam);
\r
5850 newSizeY = HIWORD(lParam);
\r
5851 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5856 case WM_GETMINMAXINFO:
\r
5857 /* Prevent resizing window too small */
\r
5858 mmi = (MINMAXINFO *) lParam;
\r
5859 mmi->ptMinTrackSize.x = 100;
\r
5860 mmi->ptMinTrackSize.y = 100;
\r
5867 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5872 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5874 if (str == NULL) str = "";
\r
5875 p = (char *) malloc(2 * strlen(str) + 2);
\r
5878 if (*str == '\n') *q++ = '\r';
\r
5882 if (commentText != NULL) free(commentText);
\r
5884 commentIndex = index;
\r
5885 commentTitle = title;
\r
5887 editComment = edit;
\r
5889 if (commentDialog) {
\r
5890 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5891 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5893 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5894 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5895 hwndMain, (DLGPROC)lpProc);
\r
5896 FreeProcInstance(lpProc);
\r
5902 /*---------------------------------------------------------------------------*\
\r
5904 * Type-in move dialog functions
\r
5906 \*---------------------------------------------------------------------------*/
\r
5909 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5911 char move[MSG_SIZ];
\r
5913 ChessMove moveType;
\r
5914 int fromX, fromY, toX, toY;
\r
5917 switch (message) {
\r
5918 case WM_INITDIALOG:
\r
5919 move[0] = (char) lParam;
\r
5920 move[1] = NULLCHAR;
\r
5921 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5922 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5923 SetWindowText(hInput, move);
\r
5925 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5929 switch (LOWORD(wParam)) {
\r
5931 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5932 { int n; Board board;
\r
5934 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5935 EditPositionPasteFEN(move);
\r
5936 EndDialog(hDlg, TRUE);
\r
5939 // [HGM] movenum: allow move number to be typed in any mode
\r
5940 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5942 EndDialog(hDlg, TRUE);
\r
5946 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5947 gameMode != Training) {
\r
5948 DisplayMoveError("Displayed move is not current");
\r
5950 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
5951 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5952 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
5953 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
5954 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5955 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
5956 if (gameMode != Training)
\r
5957 forwardMostMove = currentMove;
\r
5958 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
5960 DisplayMoveError("Could not parse move");
\r
5963 EndDialog(hDlg, TRUE);
\r
5966 EndDialog(hDlg, FALSE);
\r
5977 PopUpMoveDialog(char firstchar)
\r
5981 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
5982 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
5983 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
5984 gameMode == EditPosition || gameMode == IcsExamining ||
\r
5985 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
5986 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
5987 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
5988 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
5989 gameMode == Training) {
\r
5990 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
5991 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
5992 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
5993 FreeProcInstance(lpProc);
\r
5997 /*---------------------------------------------------------------------------*\
\r
5999 * Type-in name dialog functions
\r
6001 \*---------------------------------------------------------------------------*/
\r
6004 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6006 char move[MSG_SIZ];
\r
6009 switch (message) {
\r
6010 case WM_INITDIALOG:
\r
6011 move[0] = (char) lParam;
\r
6012 move[1] = NULLCHAR;
\r
6013 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6014 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6015 SetWindowText(hInput, move);
\r
6017 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6021 switch (LOWORD(wParam)) {
\r
6023 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6024 appData.userName = strdup(move);
\r
6027 EndDialog(hDlg, TRUE);
\r
6030 EndDialog(hDlg, FALSE);
\r
6041 PopUpNameDialog(char firstchar)
\r
6045 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6046 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6047 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6048 FreeProcInstance(lpProc);
\r
6051 /*---------------------------------------------------------------------------*\
\r
6055 \*---------------------------------------------------------------------------*/
\r
6057 /* Nonmodal error box */
\r
6058 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6059 WPARAM wParam, LPARAM lParam);
\r
6062 ErrorPopUp(char *title, char *content)
\r
6066 BOOLEAN modal = hwndMain == NULL;
\r
6084 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6085 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6088 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6090 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6091 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6092 hwndMain, (DLGPROC)lpProc);
\r
6093 FreeProcInstance(lpProc);
\r
6100 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6101 if (errorDialog == NULL) return;
\r
6102 DestroyWindow(errorDialog);
\r
6103 errorDialog = NULL;
\r
6104 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6108 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6113 switch (message) {
\r
6114 case WM_INITDIALOG:
\r
6115 GetWindowRect(hDlg, &rChild);
\r
6118 SetWindowPos(hDlg, NULL, rChild.left,
\r
6119 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6120 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6124 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6125 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6126 and it doesn't work when you resize the dialog.
\r
6127 For now, just give it a default position.
\r
6129 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6131 errorDialog = hDlg;
\r
6132 SetWindowText(hDlg, errorTitle);
\r
6133 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6134 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6138 switch (LOWORD(wParam)) {
\r
6141 if (errorDialog == hDlg) errorDialog = NULL;
\r
6142 DestroyWindow(hDlg);
\r
6154 HWND gothicDialog = NULL;
\r
6157 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6161 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6163 switch (message) {
\r
6164 case WM_INITDIALOG:
\r
6165 GetWindowRect(hDlg, &rChild);
\r
6167 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6171 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6172 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6173 and it doesn't work when you resize the dialog.
\r
6174 For now, just give it a default position.
\r
6176 gothicDialog = hDlg;
\r
6177 SetWindowText(hDlg, errorTitle);
\r
6178 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6179 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6183 switch (LOWORD(wParam)) {
\r
6186 if (errorDialog == hDlg) errorDialog = NULL;
\r
6187 DestroyWindow(hDlg);
\r
6199 GothicPopUp(char *title, VariantClass variant)
\r
6202 static char *lastTitle;
\r
6204 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6205 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6207 if(lastTitle != title && gothicDialog != NULL) {
\r
6208 DestroyWindow(gothicDialog);
\r
6209 gothicDialog = NULL;
\r
6211 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6212 title = lastTitle;
\r
6213 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6214 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6215 hwndMain, (DLGPROC)lpProc);
\r
6216 FreeProcInstance(lpProc);
\r
6221 /*---------------------------------------------------------------------------*\
\r
6223 * Ics Interaction console functions
\r
6225 \*---------------------------------------------------------------------------*/
\r
6227 #define HISTORY_SIZE 64
\r
6228 static char *history[HISTORY_SIZE];
\r
6229 int histIn = 0, histP = 0;
\r
6232 SaveInHistory(char *cmd)
\r
6234 if (history[histIn] != NULL) {
\r
6235 free(history[histIn]);
\r
6236 history[histIn] = NULL;
\r
6238 if (*cmd == NULLCHAR) return;
\r
6239 history[histIn] = StrSave(cmd);
\r
6240 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6241 if (history[histIn] != NULL) {
\r
6242 free(history[histIn]);
\r
6243 history[histIn] = NULL;
\r
6249 PrevInHistory(char *cmd)
\r
6252 if (histP == histIn) {
\r
6253 if (history[histIn] != NULL) free(history[histIn]);
\r
6254 history[histIn] = StrSave(cmd);
\r
6256 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6257 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6259 return history[histP];
\r
6265 if (histP == histIn) return NULL;
\r
6266 histP = (histP + 1) % HISTORY_SIZE;
\r
6267 return history[histP];
\r
6271 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6275 hmenu = LoadMenu(hInst, "TextMenu");
\r
6276 h = GetSubMenu(hmenu, 0);
\r
6278 if (strcmp(e->item, "-") == 0) {
\r
6279 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6281 if (e->item[0] == '|') {
\r
6282 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
\r
6283 IDM_CommandX + i, &e->item[1]);
\r
6285 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
\r
6294 WNDPROC consoleTextWindowProc;
\r
6297 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6299 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6300 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6304 SetWindowText(hInput, command);
\r
6306 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6308 sel.cpMin = 999999;
\r
6309 sel.cpMax = 999999;
\r
6310 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6315 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6316 if (sel.cpMin == sel.cpMax) {
\r
6317 /* Expand to surrounding word */
\r
6320 tr.chrg.cpMax = sel.cpMin;
\r
6321 tr.chrg.cpMin = --sel.cpMin;
\r
6322 if (sel.cpMin < 0) break;
\r
6323 tr.lpstrText = name;
\r
6324 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6325 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6329 tr.chrg.cpMin = sel.cpMax;
\r
6330 tr.chrg.cpMax = ++sel.cpMax;
\r
6331 tr.lpstrText = name;
\r
6332 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6333 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6336 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6337 MessageBeep(MB_ICONEXCLAMATION);
\r
6341 tr.lpstrText = name;
\r
6342 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6344 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6345 MessageBeep(MB_ICONEXCLAMATION);
\r
6348 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6351 sprintf(buf, "%s %s", command, name);
\r
6352 SetWindowText(hInput, buf);
\r
6353 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6355 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6356 SetWindowText(hInput, buf);
\r
6357 sel.cpMin = 999999;
\r
6358 sel.cpMax = 999999;
\r
6359 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6365 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6370 switch (message) {
\r
6372 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6375 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6378 sel.cpMin = 999999;
\r
6379 sel.cpMax = 999999;
\r
6380 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6381 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6386 if(wParam != '\022') {
\r
6387 if (wParam == '\t') {
\r
6388 if (GetKeyState(VK_SHIFT) < 0) {
\r
6390 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6391 if (buttonDesc[0].hwnd) {
\r
6392 SetFocus(buttonDesc[0].hwnd);
\r
6394 SetFocus(hwndMain);
\r
6398 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6401 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6402 JAWS_DELETE( SetFocus(hInput); )
\r
6403 SendMessage(hInput, message, wParam, lParam);
\r
6406 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6407 case WM_RBUTTONUP:
\r
6408 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6409 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6410 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6413 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6414 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6415 if (sel.cpMin == sel.cpMax) {
\r
6416 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6417 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6419 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6420 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6422 pt.x = LOWORD(lParam);
\r
6423 pt.y = HIWORD(lParam);
\r
6424 MenuPopup(hwnd, pt, hmenu, -1);
\r
6428 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6430 return SendMessage(hInput, message, wParam, lParam);
\r
6431 case WM_MBUTTONDOWN:
\r
6432 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6433 case WM_RBUTTONDOWN:
\r
6434 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6435 /* Move selection here if it was empty */
\r
6437 pt.x = LOWORD(lParam);
\r
6438 pt.y = HIWORD(lParam);
\r
6439 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6440 if (sel.cpMin == sel.cpMax) {
\r
6441 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6442 sel.cpMax = sel.cpMin;
\r
6443 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6445 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6449 switch (LOWORD(wParam)) {
\r
6450 case IDM_QuickPaste:
\r
6452 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6453 if (sel.cpMin == sel.cpMax) {
\r
6454 MessageBeep(MB_ICONEXCLAMATION);
\r
6457 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6458 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6459 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6464 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6467 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6470 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6474 int i = LOWORD(wParam) - IDM_CommandX;
\r
6475 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6476 icsTextMenuEntry[i].command != NULL) {
\r
6477 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6478 icsTextMenuEntry[i].getname,
\r
6479 icsTextMenuEntry[i].immediate);
\r
6487 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6490 WNDPROC consoleInputWindowProc;
\r
6493 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6495 char buf[MSG_SIZ];
\r
6497 static BOOL sendNextChar = FALSE;
\r
6498 static BOOL quoteNextChar = FALSE;
\r
6499 InputSource *is = consoleInputSource;
\r
6503 switch (message) {
\r
6505 if (!appData.localLineEditing || sendNextChar) {
\r
6506 is->buf[0] = (CHAR) wParam;
\r
6508 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6509 sendNextChar = FALSE;
\r
6512 if (quoteNextChar) {
\r
6513 buf[0] = (char) wParam;
\r
6514 buf[1] = NULLCHAR;
\r
6515 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6516 quoteNextChar = FALSE;
\r
6520 case '\r': /* Enter key */
\r
6521 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6522 if (consoleEcho) SaveInHistory(is->buf);
\r
6523 is->buf[is->count++] = '\n';
\r
6524 is->buf[is->count] = NULLCHAR;
\r
6525 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6526 if (consoleEcho) {
\r
6527 ConsoleOutput(is->buf, is->count, TRUE);
\r
6528 } else if (appData.localLineEditing) {
\r
6529 ConsoleOutput("\n", 1, TRUE);
\r
6532 case '\033': /* Escape key */
\r
6533 SetWindowText(hwnd, "");
\r
6534 cf.cbSize = sizeof(CHARFORMAT);
\r
6535 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6536 if (consoleEcho) {
\r
6537 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6539 cf.crTextColor = COLOR_ECHOOFF;
\r
6541 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6542 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6544 case '\t': /* Tab key */
\r
6545 if (GetKeyState(VK_SHIFT) < 0) {
\r
6547 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6550 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6551 if (buttonDesc[0].hwnd) {
\r
6552 SetFocus(buttonDesc[0].hwnd);
\r
6554 SetFocus(hwndMain);
\r
6558 case '\023': /* Ctrl+S */
\r
6559 sendNextChar = TRUE;
\r
6561 case '\021': /* Ctrl+Q */
\r
6562 quoteNextChar = TRUE;
\r
6572 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6573 p = PrevInHistory(buf);
\r
6575 SetWindowText(hwnd, p);
\r
6576 sel.cpMin = 999999;
\r
6577 sel.cpMax = 999999;
\r
6578 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6583 p = NextInHistory();
\r
6585 SetWindowText(hwnd, p);
\r
6586 sel.cpMin = 999999;
\r
6587 sel.cpMax = 999999;
\r
6588 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6594 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6598 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6602 case WM_MBUTTONDOWN:
\r
6603 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6604 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6606 case WM_RBUTTONUP:
\r
6607 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6608 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6609 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6613 hmenu = LoadMenu(hInst, "InputMenu");
\r
6614 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6615 if (sel.cpMin == sel.cpMax) {
\r
6616 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6617 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6619 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6620 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6622 pt.x = LOWORD(lParam);
\r
6623 pt.y = HIWORD(lParam);
\r
6624 MenuPopup(hwnd, pt, hmenu, -1);
\r
6628 switch (LOWORD(wParam)) {
\r
6630 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6632 case IDM_SelectAll:
\r
6634 sel.cpMax = -1; /*999999?*/
\r
6635 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6638 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6641 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6644 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6649 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6652 #define CO_MAX 100000
\r
6653 #define CO_TRIM 1000
\r
6656 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6658 static SnapData sd;
\r
6659 HWND hText, hInput;
\r
6661 static int sizeX, sizeY;
\r
6662 int newSizeX, newSizeY;
\r
6666 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6667 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6669 switch (message) {
\r
6671 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6673 ENLINK *pLink = (ENLINK*)lParam;
\r
6674 if (pLink->msg == WM_LBUTTONUP)
\r
6678 tr.chrg = pLink->chrg;
\r
6679 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6680 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6681 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6682 free(tr.lpstrText);
\r
6686 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6687 hwndConsole = hDlg;
\r
6689 consoleTextWindowProc = (WNDPROC)
\r
6690 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6691 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6692 consoleInputWindowProc = (WNDPROC)
\r
6693 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6694 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6695 Colorize(ColorNormal, TRUE);
\r
6696 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6697 ChangedConsoleFont();
\r
6698 GetClientRect(hDlg, &rect);
\r
6699 sizeX = rect.right;
\r
6700 sizeY = rect.bottom;
\r
6701 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6702 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6703 WINDOWPLACEMENT wp;
\r
6704 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6705 wp.length = sizeof(WINDOWPLACEMENT);
\r
6707 wp.showCmd = SW_SHOW;
\r
6708 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6709 wp.rcNormalPosition.left = wpConsole.x;
\r
6710 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6711 wp.rcNormalPosition.top = wpConsole.y;
\r
6712 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6713 SetWindowPlacement(hDlg, &wp);
\r
6716 // [HGM] Chessknight's change 2004-07-13
\r
6717 else { /* Determine Defaults */
\r
6718 WINDOWPLACEMENT wp;
\r
6719 wpConsole.x = wpMain.width + 1;
\r
6720 wpConsole.y = wpMain.y;
\r
6721 wpConsole.width = screenWidth - wpMain.width;
\r
6722 wpConsole.height = wpMain.height;
\r
6723 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6724 wp.length = sizeof(WINDOWPLACEMENT);
\r
6726 wp.showCmd = SW_SHOW;
\r
6727 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6728 wp.rcNormalPosition.left = wpConsole.x;
\r
6729 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6730 wp.rcNormalPosition.top = wpConsole.y;
\r
6731 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6732 SetWindowPlacement(hDlg, &wp);
\r
6735 // Allow hText to highlight URLs and send notifications on them
\r
6736 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6737 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6738 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6739 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6753 if (IsIconic(hDlg)) break;
\r
6754 newSizeX = LOWORD(lParam);
\r
6755 newSizeY = HIWORD(lParam);
\r
6756 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6757 RECT rectText, rectInput;
\r
6759 int newTextHeight, newTextWidth;
\r
6760 GetWindowRect(hText, &rectText);
\r
6761 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6762 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6763 if (newTextHeight < 0) {
\r
6764 newSizeY += -newTextHeight;
\r
6765 newTextHeight = 0;
\r
6767 SetWindowPos(hText, NULL, 0, 0,
\r
6768 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6769 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6770 pt.x = rectInput.left;
\r
6771 pt.y = rectInput.top + newSizeY - sizeY;
\r
6772 ScreenToClient(hDlg, &pt);
\r
6773 SetWindowPos(hInput, NULL,
\r
6774 pt.x, pt.y, /* needs client coords */
\r
6775 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6776 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6782 case WM_GETMINMAXINFO:
\r
6783 /* Prevent resizing window too small */
\r
6784 mmi = (MINMAXINFO *) lParam;
\r
6785 mmi->ptMinTrackSize.x = 100;
\r
6786 mmi->ptMinTrackSize.y = 100;
\r
6789 /* [AS] Snapping */
\r
6790 case WM_ENTERSIZEMOVE:
\r
6791 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6794 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6797 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6799 case WM_EXITSIZEMOVE:
\r
6800 UpdateICSWidth(hText);
\r
6801 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6804 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6812 if (hwndConsole) return;
\r
6813 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6814 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6819 ConsoleOutput(char* data, int length, int forceVisible)
\r
6824 char buf[CO_MAX+1];
\r
6827 static int delayLF = 0;
\r
6828 CHARRANGE savesel, sel;
\r
6830 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6838 while (length--) {
\r
6846 } else if (*p == '\007') {
\r
6847 MyPlaySound(&sounds[(int)SoundBell]);
\r
6854 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6855 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6856 /* Save current selection */
\r
6857 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6858 exlen = GetWindowTextLength(hText);
\r
6859 /* Find out whether current end of text is visible */
\r
6860 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6861 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6862 /* Trim existing text if it's too long */
\r
6863 if (exlen + (q - buf) > CO_MAX) {
\r
6864 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6867 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6868 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6870 savesel.cpMin -= trim;
\r
6871 savesel.cpMax -= trim;
\r
6872 if (exlen < 0) exlen = 0;
\r
6873 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6874 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6876 /* Append the new text */
\r
6877 sel.cpMin = exlen;
\r
6878 sel.cpMax = exlen;
\r
6879 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6880 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6881 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6882 if (forceVisible || exlen == 0 ||
\r
6883 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6884 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6885 /* Scroll to make new end of text visible if old end of text
\r
6886 was visible or new text is an echo of user typein */
\r
6887 sel.cpMin = 9999999;
\r
6888 sel.cpMax = 9999999;
\r
6889 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6890 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6891 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6892 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6894 if (savesel.cpMax == exlen || forceVisible) {
\r
6895 /* Move insert point to new end of text if it was at the old
\r
6896 end of text or if the new text is an echo of user typein */
\r
6897 sel.cpMin = 9999999;
\r
6898 sel.cpMax = 9999999;
\r
6899 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6901 /* Restore previous selection */
\r
6902 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6904 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6911 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6915 COLORREF oldFg, oldBg;
\r
6919 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6921 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6922 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6923 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6926 rect.right = x + squareSize;
\r
6928 rect.bottom = y + squareSize;
\r
6931 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6932 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6933 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6934 &rect, str, strlen(str), NULL);
\r
6936 (void) SetTextColor(hdc, oldFg);
\r
6937 (void) SetBkColor(hdc, oldBg);
\r
6938 (void) SelectObject(hdc, oldFont);
\r
6942 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6943 RECT *rect, char *color, char *flagFell)
\r
6947 COLORREF oldFg, oldBg;
\r
6950 if (appData.clockMode) {
\r
6952 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
6954 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
6961 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6962 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6964 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
6965 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
6967 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6971 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6972 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
6973 rect, str, strlen(str), NULL);
\r
6974 if(logoHeight > 0 && appData.clockMode) {
\r
6976 sprintf(buf, "%s %s", buf+7, flagFell);
\r
6977 r.top = rect->top + logoHeight/2;
\r
6978 r.left = rect->left;
\r
6979 r.right = rect->right;
\r
6980 r.bottom = rect->bottom;
\r
6981 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
6982 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
6983 &r, str, strlen(str), NULL);
\r
6985 (void) SetTextColor(hdc, oldFg);
\r
6986 (void) SetBkColor(hdc, oldBg);
\r
6987 (void) SelectObject(hdc, oldFont);
\r
6992 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
6998 if( count <= 0 ) {
\r
6999 if (appData.debugMode) {
\r
7000 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7003 return ERROR_INVALID_USER_BUFFER;
\r
7006 ResetEvent(ovl->hEvent);
\r
7007 ovl->Offset = ovl->OffsetHigh = 0;
\r
7008 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7012 err = GetLastError();
\r
7013 if (err == ERROR_IO_PENDING) {
\r
7014 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7018 err = GetLastError();
\r
7025 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7030 ResetEvent(ovl->hEvent);
\r
7031 ovl->Offset = ovl->OffsetHigh = 0;
\r
7032 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7036 err = GetLastError();
\r
7037 if (err == ERROR_IO_PENDING) {
\r
7038 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7042 err = GetLastError();
\r
7048 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7049 void CheckForInputBufferFull( InputSource * is )
\r
7051 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7052 /* Look for end of line */
\r
7053 char * p = is->buf;
\r
7055 while( p < is->next && *p != '\n' ) {
\r
7059 if( p >= is->next ) {
\r
7060 if (appData.debugMode) {
\r
7061 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7064 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7065 is->count = (DWORD) -1;
\r
7066 is->next = is->buf;
\r
7072 InputThread(LPVOID arg)
\r
7077 is = (InputSource *) arg;
\r
7078 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7079 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7080 while (is->hThread != NULL) {
\r
7081 is->error = DoReadFile(is->hFile, is->next,
\r
7082 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7083 &is->count, &ovl);
\r
7084 if (is->error == NO_ERROR) {
\r
7085 is->next += is->count;
\r
7087 if (is->error == ERROR_BROKEN_PIPE) {
\r
7088 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7091 is->count = (DWORD) -1;
\r
7092 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7097 CheckForInputBufferFull( is );
\r
7099 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7101 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7103 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7106 CloseHandle(ovl.hEvent);
\r
7107 CloseHandle(is->hFile);
\r
7109 if (appData.debugMode) {
\r
7110 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7117 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7119 NonOvlInputThread(LPVOID arg)
\r
7126 is = (InputSource *) arg;
\r
7127 while (is->hThread != NULL) {
\r
7128 is->error = ReadFile(is->hFile, is->next,
\r
7129 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7130 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7131 if (is->error == NO_ERROR) {
\r
7132 /* Change CRLF to LF */
\r
7133 if (is->next > is->buf) {
\r
7135 i = is->count + 1;
\r
7143 if (prev == '\r' && *p == '\n') {
\r
7155 if (is->error == ERROR_BROKEN_PIPE) {
\r
7156 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7159 is->count = (DWORD) -1;
\r
7163 CheckForInputBufferFull( is );
\r
7165 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7167 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7169 if (is->count < 0) break; /* Quit on error */
\r
7171 CloseHandle(is->hFile);
\r
7176 SocketInputThread(LPVOID arg)
\r
7180 is = (InputSource *) arg;
\r
7181 while (is->hThread != NULL) {
\r
7182 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7183 if ((int)is->count == SOCKET_ERROR) {
\r
7184 is->count = (DWORD) -1;
\r
7185 is->error = WSAGetLastError();
\r
7187 is->error = NO_ERROR;
\r
7188 is->next += is->count;
\r
7189 if (is->count == 0 && is->second == is) {
\r
7190 /* End of file on stderr; quit with no message */
\r
7194 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7196 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7198 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7204 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7208 is = (InputSource *) lParam;
\r
7209 if (is->lineByLine) {
\r
7210 /* Feed in lines one by one */
\r
7211 char *p = is->buf;
\r
7213 while (q < is->next) {
\r
7214 if (*q++ == '\n') {
\r
7215 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7220 /* Move any partial line to the start of the buffer */
\r
7222 while (p < is->next) {
\r
7227 if (is->error != NO_ERROR || is->count == 0) {
\r
7228 /* Notify backend of the error. Note: If there was a partial
\r
7229 line at the end, it is not flushed through. */
\r
7230 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7233 /* Feed in the whole chunk of input at once */
\r
7234 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7235 is->next = is->buf;
\r
7239 /*---------------------------------------------------------------------------*\
\r
7241 * Menu enables. Used when setting various modes.
\r
7243 \*---------------------------------------------------------------------------*/
\r
7251 GreyRevert(Boolean grey)
\r
7252 { // [HGM] vari: for retracting variations in local mode
\r
7253 HMENU hmenu = GetMenu(hwndMain);
\r
7254 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7258 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7260 while (enab->item > 0) {
\r
7261 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7266 Enables gnuEnables[] = {
\r
7267 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7268 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7269 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7270 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7271 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7272 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7273 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7274 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7275 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7276 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7277 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7281 Enables icsEnables[] = {
\r
7282 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7283 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7284 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7285 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7286 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7287 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7288 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7289 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7290 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7291 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7292 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7293 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7294 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7295 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7296 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7301 Enables zippyEnables[] = {
\r
7302 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7303 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7304 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7305 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7310 Enables ncpEnables[] = {
\r
7311 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7312 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7313 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7314 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7315 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7316 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7317 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7318 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7319 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7320 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7321 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7322 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7323 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7324 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7325 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7326 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7327 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7328 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7329 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7333 Enables trainingOnEnables[] = {
\r
7334 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7335 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7336 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7337 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7338 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7339 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7340 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7341 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7345 Enables trainingOffEnables[] = {
\r
7346 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7347 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7348 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7349 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7350 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7351 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7352 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7353 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7357 /* These modify either ncpEnables or gnuEnables */
\r
7358 Enables cmailEnables[] = {
\r
7359 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7360 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7361 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7362 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7363 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7364 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7365 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7369 Enables machineThinkingEnables[] = {
\r
7370 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7371 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7372 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7373 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7374 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7375 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7376 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7377 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7378 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7379 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7380 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7381 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7382 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7383 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7384 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7388 Enables userThinkingEnables[] = {
\r
7389 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7390 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7391 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7392 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7393 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7394 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7395 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7396 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7397 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7398 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7399 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7400 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7401 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7402 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7403 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7407 /*---------------------------------------------------------------------------*\
\r
7409 * Front-end interface functions exported by XBoard.
\r
7410 * Functions appear in same order as prototypes in frontend.h.
\r
7412 \*---------------------------------------------------------------------------*/
\r
7416 static UINT prevChecked = 0;
\r
7417 static int prevPausing = 0;
\r
7420 if (pausing != prevPausing) {
\r
7421 prevPausing = pausing;
\r
7422 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7423 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7424 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7427 switch (gameMode) {
\r
7428 case BeginningOfGame:
\r
7429 if (appData.icsActive)
\r
7430 nowChecked = IDM_IcsClient;
\r
7431 else if (appData.noChessProgram)
\r
7432 nowChecked = IDM_EditGame;
\r
7434 nowChecked = IDM_MachineBlack;
\r
7436 case MachinePlaysBlack:
\r
7437 nowChecked = IDM_MachineBlack;
\r
7439 case MachinePlaysWhite:
\r
7440 nowChecked = IDM_MachineWhite;
\r
7442 case TwoMachinesPlay:
\r
7443 nowChecked = IDM_TwoMachines;
\r
7446 nowChecked = IDM_AnalysisMode;
\r
7449 nowChecked = IDM_AnalyzeFile;
\r
7452 nowChecked = IDM_EditGame;
\r
7454 case PlayFromGameFile:
\r
7455 nowChecked = IDM_LoadGame;
\r
7457 case EditPosition:
\r
7458 nowChecked = IDM_EditPosition;
\r
7461 nowChecked = IDM_Training;
\r
7463 case IcsPlayingWhite:
\r
7464 case IcsPlayingBlack:
\r
7465 case IcsObserving:
\r
7467 nowChecked = IDM_IcsClient;
\r
7474 if (prevChecked != 0)
\r
7475 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7476 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7477 if (nowChecked != 0)
\r
7478 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7479 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7481 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7482 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7483 MF_BYCOMMAND|MF_ENABLED);
\r
7485 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7486 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7489 prevChecked = nowChecked;
\r
7491 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7492 if (appData.icsActive) {
\r
7493 if (appData.icsEngineAnalyze) {
\r
7494 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7495 MF_BYCOMMAND|MF_CHECKED);
\r
7497 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7498 MF_BYCOMMAND|MF_UNCHECKED);
\r
7506 HMENU hmenu = GetMenu(hwndMain);
\r
7507 SetMenuEnables(hmenu, icsEnables);
\r
7508 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7509 MF_BYPOSITION|MF_ENABLED);
\r
7511 if (appData.zippyPlay) {
\r
7512 SetMenuEnables(hmenu, zippyEnables);
\r
7513 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7514 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7515 MF_BYCOMMAND|MF_ENABLED);
\r
7523 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7529 HMENU hmenu = GetMenu(hwndMain);
\r
7530 SetMenuEnables(hmenu, ncpEnables);
\r
7531 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7532 MF_BYPOSITION|MF_GRAYED);
\r
7533 DrawMenuBar(hwndMain);
\r
7539 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7543 SetTrainingModeOn()
\r
7546 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7547 for (i = 0; i < N_BUTTONS; i++) {
\r
7548 if (buttonDesc[i].hwnd != NULL)
\r
7549 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7554 VOID SetTrainingModeOff()
\r
7557 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7558 for (i = 0; i < N_BUTTONS; i++) {
\r
7559 if (buttonDesc[i].hwnd != NULL)
\r
7560 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7566 SetUserThinkingEnables()
\r
7568 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7572 SetMachineThinkingEnables()
\r
7574 HMENU hMenu = GetMenu(hwndMain);
\r
7575 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7577 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7579 if (gameMode == MachinePlaysBlack) {
\r
7580 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7581 } else if (gameMode == MachinePlaysWhite) {
\r
7582 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7583 } else if (gameMode == TwoMachinesPlay) {
\r
7584 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7590 DisplayTitle(char *str)
\r
7592 char title[MSG_SIZ], *host;
\r
7593 if (str[0] != NULLCHAR) {
\r
7594 strcpy(title, str);
\r
7595 } else if (appData.icsActive) {
\r
7596 if (appData.icsCommPort[0] != NULLCHAR)
\r
7599 host = appData.icsHost;
\r
7600 sprintf(title, "%s: %s", szTitle, host);
\r
7601 } else if (appData.noChessProgram) {
\r
7602 strcpy(title, szTitle);
\r
7604 strcpy(title, szTitle);
\r
7605 strcat(title, ": ");
\r
7606 strcat(title, first.tidy);
\r
7608 SetWindowText(hwndMain, title);
\r
7613 DisplayMessage(char *str1, char *str2)
\r
7617 int remain = MESSAGE_TEXT_MAX - 1;
\r
7620 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7621 messageText[0] = NULLCHAR;
\r
7623 len = strlen(str1);
\r
7624 if (len > remain) len = remain;
\r
7625 strncpy(messageText, str1, len);
\r
7626 messageText[len] = NULLCHAR;
\r
7629 if (*str2 && remain >= 2) {
\r
7631 strcat(messageText, " ");
\r
7634 len = strlen(str2);
\r
7635 if (len > remain) len = remain;
\r
7636 strncat(messageText, str2, len);
\r
7638 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7640 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7644 hdc = GetDC(hwndMain);
\r
7645 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7646 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7647 &messageRect, messageText, strlen(messageText), NULL);
\r
7648 (void) SelectObject(hdc, oldFont);
\r
7649 (void) ReleaseDC(hwndMain, hdc);
\r
7653 DisplayError(char *str, int error)
\r
7655 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7661 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7662 NULL, error, LANG_NEUTRAL,
\r
7663 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7665 sprintf(buf, "%s:\n%s", str, buf2);
\r
7667 ErrorMap *em = errmap;
\r
7668 while (em->err != 0 && em->err != error) em++;
\r
7669 if (em->err != 0) {
\r
7670 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7672 sprintf(buf, "%s:\nError code %d", str, error);
\r
7677 ErrorPopUp("Error", buf);
\r
7682 DisplayMoveError(char *str)
\r
7684 fromX = fromY = -1;
\r
7685 ClearHighlights();
\r
7686 DrawPosition(FALSE, NULL);
\r
7687 if (appData.popupMoveErrors) {
\r
7688 ErrorPopUp("Error", str);
\r
7690 DisplayMessage(str, "");
\r
7691 moveErrorMessageUp = TRUE;
\r
7696 DisplayFatalError(char *str, int error, int exitStatus)
\r
7698 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7700 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7703 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7704 NULL, error, LANG_NEUTRAL,
\r
7705 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7707 sprintf(buf, "%s:\n%s", str, buf2);
\r
7709 ErrorMap *em = errmap;
\r
7710 while (em->err != 0 && em->err != error) em++;
\r
7711 if (em->err != 0) {
\r
7712 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7714 sprintf(buf, "%s:\nError code %d", str, error);
\r
7719 if (appData.debugMode) {
\r
7720 fprintf(debugFP, "%s: %s\n", label, str);
\r
7722 if (appData.popupExitMessage) {
\r
7723 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7724 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7726 ExitEvent(exitStatus);
\r
7731 DisplayInformation(char *str)
\r
7733 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7738 DisplayNote(char *str)
\r
7740 ErrorPopUp("Note", str);
\r
7745 char *title, *question, *replyPrefix;
\r
7750 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7752 static QuestionParams *qp;
\r
7753 char reply[MSG_SIZ];
\r
7756 switch (message) {
\r
7757 case WM_INITDIALOG:
\r
7758 qp = (QuestionParams *) lParam;
\r
7759 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7760 SetWindowText(hDlg, qp->title);
\r
7761 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7762 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7766 switch (LOWORD(wParam)) {
\r
7768 strcpy(reply, qp->replyPrefix);
\r
7769 if (*reply) strcat(reply, " ");
\r
7770 len = strlen(reply);
\r
7771 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7772 strcat(reply, "\n");
\r
7773 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7774 EndDialog(hDlg, TRUE);
\r
7775 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7778 EndDialog(hDlg, FALSE);
\r
7789 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7791 QuestionParams qp;
\r
7795 qp.question = question;
\r
7796 qp.replyPrefix = replyPrefix;
\r
7798 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7799 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7800 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7801 FreeProcInstance(lpProc);
\r
7804 /* [AS] Pick FRC position */
\r
7805 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7807 static int * lpIndexFRC;
\r
7813 case WM_INITDIALOG:
\r
7814 lpIndexFRC = (int *) lParam;
\r
7816 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7818 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7819 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7820 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7821 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7826 switch( LOWORD(wParam) ) {
\r
7828 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7829 EndDialog( hDlg, 0 );
\r
7830 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7833 EndDialog( hDlg, 1 );
\r
7835 case IDC_NFG_Edit:
\r
7836 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7837 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7839 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7842 case IDC_NFG_Random:
\r
7843 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7844 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7857 int index = appData.defaultFrcPosition;
\r
7858 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7860 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7862 if( result == 0 ) {
\r
7863 appData.defaultFrcPosition = index;
\r
7869 /* [AS] Game list options */
\r
7875 static GLT_Item GLT_ItemInfo[] = {
\r
7876 { GLT_EVENT, "Event" },
\r
7877 { GLT_SITE, "Site" },
\r
7878 { GLT_DATE, "Date" },
\r
7879 { GLT_ROUND, "Round" },
\r
7880 { GLT_PLAYERS, "Players" },
\r
7881 { GLT_RESULT, "Result" },
\r
7882 { GLT_WHITE_ELO, "White Rating" },
\r
7883 { GLT_BLACK_ELO, "Black Rating" },
\r
7884 { GLT_TIME_CONTROL,"Time Control" },
\r
7885 { GLT_VARIANT, "Variant" },
\r
7886 { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },
\r
7887 { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom
\r
7891 const char * GLT_FindItem( char id )
\r
7893 const char * result = 0;
\r
7895 GLT_Item * list = GLT_ItemInfo;
\r
7897 while( list->id != 0 ) {
\r
7898 if( list->id == id ) {
\r
7899 result = list->name;
\r
7909 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )
\r
7911 const char * name = GLT_FindItem( id );
\r
7914 if( index >= 0 ) {
\r
7915 SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );
\r
7918 SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );
\r
7923 void GLT_TagsToList( HWND hDlg, char * tags )
\r
7927 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7930 GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
\r
7934 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );
\r
7936 pc = GLT_ALL_TAGS;
\r
7939 if( strchr( tags, *pc ) == 0 ) {
\r
7940 GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );
\r
7945 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7948 char GLT_ListItemToTag( HWND hDlg, int index )
\r
7950 char result = '\0';
\r
7953 GLT_Item * list = GLT_ItemInfo;
\r
7955 if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {
\r
7956 while( list->id != 0 ) {
\r
7957 if( strcmp( list->name, name ) == 0 ) {
\r
7958 result = list->id;
\r
7969 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7971 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7972 int idx2 = idx1 + delta;
\r
7973 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7975 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7978 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7979 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7980 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7981 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7985 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7987 static char glt[64];
\r
7988 static char * lpUserGLT;
\r
7992 case WM_INITDIALOG:
\r
7993 lpUserGLT = (char *) lParam;
\r
7995 strcpy( glt, lpUserGLT );
\r
7997 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7999 /* Initialize list */
\r
8000 GLT_TagsToList( hDlg, glt );
\r
8002 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8007 switch( LOWORD(wParam) ) {
\r
8010 char * pc = lpUserGLT;
\r
8012 // int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8016 id = GLT_ListItemToTag( hDlg, idx );
\r
8020 } while( id != '\0' );
\r
8022 EndDialog( hDlg, 0 );
\r
8025 EndDialog( hDlg, 1 );
\r
8028 case IDC_GLT_Default:
\r
8029 strcpy( glt, GLT_DEFAULT_TAGS );
\r
8030 GLT_TagsToList( hDlg, glt );
\r
8033 case IDC_GLT_Restore:
\r
8034 strcpy( glt, lpUserGLT );
\r
8035 GLT_TagsToList( hDlg, glt );
\r
8039 GLT_MoveSelection( hDlg, -1 );
\r
8042 case IDC_GLT_Down:
\r
8043 GLT_MoveSelection( hDlg, +1 );
\r
8053 int GameListOptions()
\r
8057 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8059 strcpy( glt, appData.gameListTags );
\r
8061 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );
\r
8063 if( result == 0 ) {
\r
8064 /* [AS] Memory leak here! */
\r
8065 appData.gameListTags = strdup( glt );
\r
8073 DisplayIcsInteractionTitle(char *str)
\r
8075 char consoleTitle[MSG_SIZ];
\r
8077 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8078 SetWindowText(hwndConsole, consoleTitle);
\r
8082 DrawPosition(int fullRedraw, Board board)
\r
8084 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8087 void NotifyFrontendLogin()
\r
8090 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8096 fromX = fromY = -1;
\r
8097 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8098 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8099 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8100 dragInfo.lastpos = dragInfo.pos;
\r
8101 dragInfo.start.x = dragInfo.start.y = -1;
\r
8102 dragInfo.from = dragInfo.start;
\r
8104 DrawPosition(TRUE, NULL);
\r
8110 CommentPopUp(char *title, char *str)
\r
8112 HWND hwnd = GetActiveWindow();
\r
8113 EitherCommentPopUp(0, title, str, FALSE);
\r
8115 SetActiveWindow(hwnd);
\r
8119 CommentPopDown(void)
\r
8121 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8122 if (commentDialog) {
\r
8123 ShowWindow(commentDialog, SW_HIDE);
\r
8125 commentUp = FALSE;
\r
8129 EditCommentPopUp(int index, char *title, char *str)
\r
8131 EitherCommentPopUp(index, title, str, TRUE);
\r
8138 MyPlaySound(&sounds[(int)SoundMove]);
\r
8141 VOID PlayIcsWinSound()
\r
8143 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8146 VOID PlayIcsLossSound()
\r
8148 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8151 VOID PlayIcsDrawSound()
\r
8153 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8156 VOID PlayIcsUnfinishedSound()
\r
8158 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8164 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8172 consoleEcho = TRUE;
\r
8173 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8174 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8175 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8184 consoleEcho = FALSE;
\r
8185 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8186 /* This works OK: set text and background both to the same color */
\r
8188 cf.crTextColor = COLOR_ECHOOFF;
\r
8189 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8190 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8193 /* No Raw()...? */
\r
8195 void Colorize(ColorClass cc, int continuation)
\r
8197 currentColorClass = cc;
\r
8198 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8199 consoleCF.crTextColor = textAttribs[cc].color;
\r
8200 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8201 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8207 static char buf[MSG_SIZ];
\r
8208 DWORD bufsiz = MSG_SIZ;
\r
8210 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8211 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8213 if (!GetUserName(buf, &bufsiz)) {
\r
8214 /*DisplayError("Error getting user name", GetLastError());*/
\r
8215 strcpy(buf, "User");
\r
8223 static char buf[MSG_SIZ];
\r
8224 DWORD bufsiz = MSG_SIZ;
\r
8226 if (!GetComputerName(buf, &bufsiz)) {
\r
8227 /*DisplayError("Error getting host name", GetLastError());*/
\r
8228 strcpy(buf, "Unknown");
\r
8235 ClockTimerRunning()
\r
8237 return clockTimerEvent != 0;
\r
8243 if (clockTimerEvent == 0) return FALSE;
\r
8244 KillTimer(hwndMain, clockTimerEvent);
\r
8245 clockTimerEvent = 0;
\r
8250 StartClockTimer(long millisec)
\r
8252 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8253 (UINT) millisec, NULL);
\r
8257 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8260 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8262 if(appData.noGUI) return;
\r
8263 hdc = GetDC(hwndMain);
\r
8264 if (!IsIconic(hwndMain)) {
\r
8265 DisplayAClock(hdc, timeRemaining, highlight,
\r
8266 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8268 if (highlight && iconCurrent == iconBlack) {
\r
8269 iconCurrent = iconWhite;
\r
8270 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8271 if (IsIconic(hwndMain)) {
\r
8272 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8275 (void) ReleaseDC(hwndMain, hdc);
\r
8277 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8281 DisplayBlackClock(long timeRemaining, int highlight)
\r
8284 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8286 if(appData.noGUI) return;
\r
8287 hdc = GetDC(hwndMain);
\r
8288 if (!IsIconic(hwndMain)) {
\r
8289 DisplayAClock(hdc, timeRemaining, highlight,
\r
8290 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8292 if (highlight && iconCurrent == iconWhite) {
\r
8293 iconCurrent = iconBlack;
\r
8294 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8295 if (IsIconic(hwndMain)) {
\r
8296 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8299 (void) ReleaseDC(hwndMain, hdc);
\r
8301 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8306 LoadGameTimerRunning()
\r
8308 return loadGameTimerEvent != 0;
\r
8312 StopLoadGameTimer()
\r
8314 if (loadGameTimerEvent == 0) return FALSE;
\r
8315 KillTimer(hwndMain, loadGameTimerEvent);
\r
8316 loadGameTimerEvent = 0;
\r
8321 StartLoadGameTimer(long millisec)
\r
8323 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8324 (UINT) millisec, NULL);
\r
8332 char fileTitle[MSG_SIZ];
\r
8334 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8335 f = OpenFileDialog(hwndMain, "a", defName,
\r
8336 appData.oldSaveStyle ? "gam" : "pgn",
\r
8338 "Save Game to File", NULL, fileTitle, NULL);
\r
8340 SaveGame(f, 0, "");
\r
8347 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8349 if (delayedTimerEvent != 0) {
\r
8350 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8351 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8353 KillTimer(hwndMain, delayedTimerEvent);
\r
8354 delayedTimerEvent = 0;
\r
8355 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8356 delayedTimerCallback();
\r
8358 delayedTimerCallback = cb;
\r
8359 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8360 (UINT) millisec, NULL);
\r
8363 DelayedEventCallback
\r
8366 if (delayedTimerEvent) {
\r
8367 return delayedTimerCallback;
\r
8374 CancelDelayedEvent()
\r
8376 if (delayedTimerEvent) {
\r
8377 KillTimer(hwndMain, delayedTimerEvent);
\r
8378 delayedTimerEvent = 0;
\r
8382 DWORD GetWin32Priority(int nice)
\r
8383 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8385 REALTIME_PRIORITY_CLASS 0x00000100
\r
8386 HIGH_PRIORITY_CLASS 0x00000080
\r
8387 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8388 NORMAL_PRIORITY_CLASS 0x00000020
\r
8389 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8390 IDLE_PRIORITY_CLASS 0x00000040
\r
8392 if (nice < -15) return 0x00000080;
\r
8393 if (nice < 0) return 0x00008000;
\r
8394 if (nice == 0) return 0x00000020;
\r
8395 if (nice < 15) return 0x00004000;
\r
8396 return 0x00000040;
\r
8399 /* Start a child process running the given program.
\r
8400 The process's standard output can be read from "from", and its
\r
8401 standard input can be written to "to".
\r
8402 Exit with fatal error if anything goes wrong.
\r
8403 Returns an opaque pointer that can be used to destroy the process
\r
8407 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8409 #define BUFSIZE 4096
\r
8411 HANDLE hChildStdinRd, hChildStdinWr,
\r
8412 hChildStdoutRd, hChildStdoutWr;
\r
8413 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8414 SECURITY_ATTRIBUTES saAttr;
\r
8416 PROCESS_INFORMATION piProcInfo;
\r
8417 STARTUPINFO siStartInfo;
\r
8419 char buf[MSG_SIZ];
\r
8422 if (appData.debugMode) {
\r
8423 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8428 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8429 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8430 saAttr.bInheritHandle = TRUE;
\r
8431 saAttr.lpSecurityDescriptor = NULL;
\r
8434 * The steps for redirecting child's STDOUT:
\r
8435 * 1. Create anonymous pipe to be STDOUT for child.
\r
8436 * 2. Create a noninheritable duplicate of read handle,
\r
8437 * and close the inheritable read handle.
\r
8440 /* Create a pipe for the child's STDOUT. */
\r
8441 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8442 return GetLastError();
\r
8445 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8446 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8447 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8448 FALSE, /* not inherited */
\r
8449 DUPLICATE_SAME_ACCESS);
\r
8451 return GetLastError();
\r
8453 CloseHandle(hChildStdoutRd);
\r
8456 * The steps for redirecting child's STDIN:
\r
8457 * 1. Create anonymous pipe to be STDIN for child.
\r
8458 * 2. Create a noninheritable duplicate of write handle,
\r
8459 * and close the inheritable write handle.
\r
8462 /* Create a pipe for the child's STDIN. */
\r
8463 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8464 return GetLastError();
\r
8467 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8468 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8469 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8470 FALSE, /* not inherited */
\r
8471 DUPLICATE_SAME_ACCESS);
\r
8473 return GetLastError();
\r
8475 CloseHandle(hChildStdinWr);
\r
8477 /* Arrange to (1) look in dir for the child .exe file, and
\r
8478 * (2) have dir be the child's working directory. Interpret
\r
8479 * dir relative to the directory WinBoard loaded from. */
\r
8480 GetCurrentDirectory(MSG_SIZ, buf);
\r
8481 SetCurrentDirectory(installDir);
\r
8482 SetCurrentDirectory(dir);
\r
8484 /* Now create the child process. */
\r
8486 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8487 siStartInfo.lpReserved = NULL;
\r
8488 siStartInfo.lpDesktop = NULL;
\r
8489 siStartInfo.lpTitle = NULL;
\r
8490 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8491 siStartInfo.cbReserved2 = 0;
\r
8492 siStartInfo.lpReserved2 = NULL;
\r
8493 siStartInfo.hStdInput = hChildStdinRd;
\r
8494 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8495 siStartInfo.hStdError = hChildStdoutWr;
\r
8497 fSuccess = CreateProcess(NULL,
\r
8498 cmdLine, /* command line */
\r
8499 NULL, /* process security attributes */
\r
8500 NULL, /* primary thread security attrs */
\r
8501 TRUE, /* handles are inherited */
\r
8502 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8503 NULL, /* use parent's environment */
\r
8505 &siStartInfo, /* STARTUPINFO pointer */
\r
8506 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8508 err = GetLastError();
\r
8509 SetCurrentDirectory(buf); /* return to prev directory */
\r
8514 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8515 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8516 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8519 /* Close the handles we don't need in the parent */
\r
8520 CloseHandle(piProcInfo.hThread);
\r
8521 CloseHandle(hChildStdinRd);
\r
8522 CloseHandle(hChildStdoutWr);
\r
8524 /* Prepare return value */
\r
8525 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8526 cp->kind = CPReal;
\r
8527 cp->hProcess = piProcInfo.hProcess;
\r
8528 cp->pid = piProcInfo.dwProcessId;
\r
8529 cp->hFrom = hChildStdoutRdDup;
\r
8530 cp->hTo = hChildStdinWrDup;
\r
8532 *pr = (void *) cp;
\r
8534 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8535 2000 where engines sometimes don't see the initial command(s)
\r
8536 from WinBoard and hang. I don't understand how that can happen,
\r
8537 but the Sleep is harmless, so I've put it in. Others have also
\r
8538 reported what may be the same problem, so hopefully this will fix
\r
8539 it for them too. */
\r
8547 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8549 ChildProc *cp; int result;
\r
8551 cp = (ChildProc *) pr;
\r
8552 if (cp == NULL) return;
\r
8554 switch (cp->kind) {
\r
8556 /* TerminateProcess is considered harmful, so... */
\r
8557 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8558 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8559 /* The following doesn't work because the chess program
\r
8560 doesn't "have the same console" as WinBoard. Maybe
\r
8561 we could arrange for this even though neither WinBoard
\r
8562 nor the chess program uses a console for stdio? */
\r
8563 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8565 /* [AS] Special termination modes for misbehaving programs... */
\r
8566 if( signal == 9 ) {
\r
8567 result = TerminateProcess( cp->hProcess, 0 );
\r
8569 if ( appData.debugMode) {
\r
8570 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8573 else if( signal == 10 ) {
\r
8574 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8576 if( dw != WAIT_OBJECT_0 ) {
\r
8577 result = TerminateProcess( cp->hProcess, 0 );
\r
8579 if ( appData.debugMode) {
\r
8580 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8586 CloseHandle(cp->hProcess);
\r
8590 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8594 closesocket(cp->sock);
\r
8599 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8600 closesocket(cp->sock);
\r
8601 closesocket(cp->sock2);
\r
8609 InterruptChildProcess(ProcRef pr)
\r
8613 cp = (ChildProc *) pr;
\r
8614 if (cp == NULL) return;
\r
8615 switch (cp->kind) {
\r
8617 /* The following doesn't work because the chess program
\r
8618 doesn't "have the same console" as WinBoard. Maybe
\r
8619 we could arrange for this even though neither WinBoard
\r
8620 nor the chess program uses a console for stdio */
\r
8621 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8626 /* Can't interrupt */
\r
8630 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8637 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8639 char cmdLine[MSG_SIZ];
\r
8641 if (port[0] == NULLCHAR) {
\r
8642 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8644 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8646 return StartChildProcess(cmdLine, "", pr);
\r
8650 /* Code to open TCP sockets */
\r
8653 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8658 struct sockaddr_in sa, mysa;
\r
8659 struct hostent FAR *hp;
\r
8660 unsigned short uport;
\r
8661 WORD wVersionRequested;
\r
8664 /* Initialize socket DLL */
\r
8665 wVersionRequested = MAKEWORD(1, 1);
\r
8666 err = WSAStartup(wVersionRequested, &wsaData);
\r
8667 if (err != 0) return err;
\r
8670 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8671 err = WSAGetLastError();
\r
8676 /* Bind local address using (mostly) don't-care values.
\r
8678 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8679 mysa.sin_family = AF_INET;
\r
8680 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8681 uport = (unsigned short) 0;
\r
8682 mysa.sin_port = htons(uport);
\r
8683 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8684 == SOCKET_ERROR) {
\r
8685 err = WSAGetLastError();
\r
8690 /* Resolve remote host name */
\r
8691 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8692 if (!(hp = gethostbyname(host))) {
\r
8693 unsigned int b0, b1, b2, b3;
\r
8695 err = WSAGetLastError();
\r
8697 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8698 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8699 hp->h_addrtype = AF_INET;
\r
8701 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8702 hp->h_addr_list[0] = (char *) malloc(4);
\r
8703 hp->h_addr_list[0][0] = (char) b0;
\r
8704 hp->h_addr_list[0][1] = (char) b1;
\r
8705 hp->h_addr_list[0][2] = (char) b2;
\r
8706 hp->h_addr_list[0][3] = (char) b3;
\r
8712 sa.sin_family = hp->h_addrtype;
\r
8713 uport = (unsigned short) atoi(port);
\r
8714 sa.sin_port = htons(uport);
\r
8715 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8717 /* Make connection */
\r
8718 if (connect(s, (struct sockaddr *) &sa,
\r
8719 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8720 err = WSAGetLastError();
\r
8725 /* Prepare return value */
\r
8726 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8727 cp->kind = CPSock;
\r
8729 *pr = (ProcRef *) cp;
\r
8735 OpenCommPort(char *name, ProcRef *pr)
\r
8740 char fullname[MSG_SIZ];
\r
8742 if (*name != '\\')
\r
8743 sprintf(fullname, "\\\\.\\%s", name);
\r
8745 strcpy(fullname, name);
\r
8747 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8748 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8749 if (h == (HANDLE) -1) {
\r
8750 return GetLastError();
\r
8754 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8756 /* Accumulate characters until a 100ms pause, then parse */
\r
8757 ct.ReadIntervalTimeout = 100;
\r
8758 ct.ReadTotalTimeoutMultiplier = 0;
\r
8759 ct.ReadTotalTimeoutConstant = 0;
\r
8760 ct.WriteTotalTimeoutMultiplier = 0;
\r
8761 ct.WriteTotalTimeoutConstant = 0;
\r
8762 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8764 /* Prepare return value */
\r
8765 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8766 cp->kind = CPComm;
\r
8769 *pr = (ProcRef *) cp;
\r
8775 OpenLoopback(ProcRef *pr)
\r
8777 DisplayFatalError("Not implemented", 0, 1);
\r
8783 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8788 struct sockaddr_in sa, mysa;
\r
8789 struct hostent FAR *hp;
\r
8790 unsigned short uport;
\r
8791 WORD wVersionRequested;
\r
8794 char stderrPortStr[MSG_SIZ];
\r
8796 /* Initialize socket DLL */
\r
8797 wVersionRequested = MAKEWORD(1, 1);
\r
8798 err = WSAStartup(wVersionRequested, &wsaData);
\r
8799 if (err != 0) return err;
\r
8801 /* Resolve remote host name */
\r
8802 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8803 if (!(hp = gethostbyname(host))) {
\r
8804 unsigned int b0, b1, b2, b3;
\r
8806 err = WSAGetLastError();
\r
8808 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8809 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8810 hp->h_addrtype = AF_INET;
\r
8812 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8813 hp->h_addr_list[0] = (char *) malloc(4);
\r
8814 hp->h_addr_list[0][0] = (char) b0;
\r
8815 hp->h_addr_list[0][1] = (char) b1;
\r
8816 hp->h_addr_list[0][2] = (char) b2;
\r
8817 hp->h_addr_list[0][3] = (char) b3;
\r
8823 sa.sin_family = hp->h_addrtype;
\r
8824 uport = (unsigned short) 514;
\r
8825 sa.sin_port = htons(uport);
\r
8826 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8828 /* Bind local socket to unused "privileged" port address
\r
8830 s = INVALID_SOCKET;
\r
8831 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8832 mysa.sin_family = AF_INET;
\r
8833 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8834 for (fromPort = 1023;; fromPort--) {
\r
8835 if (fromPort < 0) {
\r
8837 return WSAEADDRINUSE;
\r
8839 if (s == INVALID_SOCKET) {
\r
8840 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8841 err = WSAGetLastError();
\r
8846 uport = (unsigned short) fromPort;
\r
8847 mysa.sin_port = htons(uport);
\r
8848 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8849 == SOCKET_ERROR) {
\r
8850 err = WSAGetLastError();
\r
8851 if (err == WSAEADDRINUSE) continue;
\r
8855 if (connect(s, (struct sockaddr *) &sa,
\r
8856 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8857 err = WSAGetLastError();
\r
8858 if (err == WSAEADDRINUSE) {
\r
8869 /* Bind stderr local socket to unused "privileged" port address
\r
8871 s2 = INVALID_SOCKET;
\r
8872 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8873 mysa.sin_family = AF_INET;
\r
8874 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8875 for (fromPort = 1023;; fromPort--) {
\r
8876 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8877 if (fromPort < 0) {
\r
8878 (void) closesocket(s);
\r
8880 return WSAEADDRINUSE;
\r
8882 if (s2 == INVALID_SOCKET) {
\r
8883 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8884 err = WSAGetLastError();
\r
8890 uport = (unsigned short) fromPort;
\r
8891 mysa.sin_port = htons(uport);
\r
8892 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8893 == SOCKET_ERROR) {
\r
8894 err = WSAGetLastError();
\r
8895 if (err == WSAEADDRINUSE) continue;
\r
8896 (void) closesocket(s);
\r
8900 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8901 err = WSAGetLastError();
\r
8902 if (err == WSAEADDRINUSE) {
\r
8904 s2 = INVALID_SOCKET;
\r
8907 (void) closesocket(s);
\r
8908 (void) closesocket(s2);
\r
8914 prevStderrPort = fromPort; // remember port used
\r
8915 sprintf(stderrPortStr, "%d", fromPort);
\r
8917 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8918 err = WSAGetLastError();
\r
8919 (void) closesocket(s);
\r
8920 (void) closesocket(s2);
\r
8925 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8926 err = WSAGetLastError();
\r
8927 (void) closesocket(s);
\r
8928 (void) closesocket(s2);
\r
8932 if (*user == NULLCHAR) user = UserName();
\r
8933 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8934 err = WSAGetLastError();
\r
8935 (void) closesocket(s);
\r
8936 (void) closesocket(s2);
\r
8940 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8941 err = WSAGetLastError();
\r
8942 (void) closesocket(s);
\r
8943 (void) closesocket(s2);
\r
8948 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8949 err = WSAGetLastError();
\r
8950 (void) closesocket(s);
\r
8951 (void) closesocket(s2);
\r
8955 (void) closesocket(s2); /* Stop listening */
\r
8957 /* Prepare return value */
\r
8958 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8959 cp->kind = CPRcmd;
\r
8962 *pr = (ProcRef *) cp;
\r
8969 AddInputSource(ProcRef pr, int lineByLine,
\r
8970 InputCallback func, VOIDSTAR closure)
\r
8972 InputSource *is, *is2 = NULL;
\r
8973 ChildProc *cp = (ChildProc *) pr;
\r
8975 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8976 is->lineByLine = lineByLine;
\r
8978 is->closure = closure;
\r
8979 is->second = NULL;
\r
8980 is->next = is->buf;
\r
8981 if (pr == NoProc) {
\r
8982 is->kind = CPReal;
\r
8983 consoleInputSource = is;
\r
8985 is->kind = cp->kind;
\r
8987 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8988 we create all threads suspended so that the is->hThread variable can be
\r
8989 safely assigned, then let the threads start with ResumeThread.
\r
8991 switch (cp->kind) {
\r
8993 is->hFile = cp->hFrom;
\r
8994 cp->hFrom = NULL; /* now owned by InputThread */
\r
8996 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8997 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9001 is->hFile = cp->hFrom;
\r
9002 cp->hFrom = NULL; /* now owned by InputThread */
\r
9004 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9005 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9009 is->sock = cp->sock;
\r
9011 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9012 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9016 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9018 is->sock = cp->sock;
\r
9020 is2->sock = cp->sock2;
\r
9021 is2->second = is2;
\r
9023 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9024 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9026 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9027 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9031 if( is->hThread != NULL ) {
\r
9032 ResumeThread( is->hThread );
\r
9035 if( is2 != NULL && is2->hThread != NULL ) {
\r
9036 ResumeThread( is2->hThread );
\r
9040 return (InputSourceRef) is;
\r
9044 RemoveInputSource(InputSourceRef isr)
\r
9048 is = (InputSource *) isr;
\r
9049 is->hThread = NULL; /* tell thread to stop */
\r
9050 CloseHandle(is->hThread);
\r
9051 if (is->second != NULL) {
\r
9052 is->second->hThread = NULL;
\r
9053 CloseHandle(is->second->hThread);
\r
9057 int no_wrap(char *message, int count)
\r
9059 ConsoleOutput(message, count, FALSE);
\r
9064 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9067 int outCount = SOCKET_ERROR;
\r
9068 ChildProc *cp = (ChildProc *) pr;
\r
9069 static OVERLAPPED ovl;
\r
9070 static int line = 0;
\r
9074 if (appData.noJoin || !appData.useInternalWrap)
\r
9075 return no_wrap(message, count);
\r
9078 int width = get_term_width();
\r
9079 int len = wrap(NULL, message, count, width, &line);
\r
9080 char *msg = malloc(len);
\r
9084 return no_wrap(message, count);
\r
9087 dbgchk = wrap(msg, message, count, width, &line);
\r
9088 if (dbgchk != len && appData.debugMode)
\r
9089 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9090 ConsoleOutput(msg, len, FALSE);
\r
9097 if (ovl.hEvent == NULL) {
\r
9098 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9100 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9102 switch (cp->kind) {
\r
9105 outCount = send(cp->sock, message, count, 0);
\r
9106 if (outCount == SOCKET_ERROR) {
\r
9107 *outError = WSAGetLastError();
\r
9109 *outError = NO_ERROR;
\r
9114 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9115 &dOutCount, NULL)) {
\r
9116 *outError = NO_ERROR;
\r
9117 outCount = (int) dOutCount;
\r
9119 *outError = GetLastError();
\r
9124 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9125 &dOutCount, &ovl);
\r
9126 if (*outError == NO_ERROR) {
\r
9127 outCount = (int) dOutCount;
\r
9135 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9138 /* Ignore delay, not implemented for WinBoard */
\r
9139 return OutputToProcess(pr, message, count, outError);
\r
9144 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9145 char *buf, int count, int error)
\r
9147 DisplayFatalError("Not implemented", 0, 1);
\r
9150 /* see wgamelist.c for Game List functions */
\r
9151 /* see wedittags.c for Edit Tags functions */
\r
9158 char buf[MSG_SIZ];
\r
9161 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9162 f = fopen(buf, "r");
\r
9164 ProcessICSInitScript(f);
\r
9172 StartAnalysisClock()
\r
9174 if (analysisTimerEvent) return;
\r
9175 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9176 (UINT) 2000, NULL);
\r
9180 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9182 highlightInfo.sq[0].x = fromX;
\r
9183 highlightInfo.sq[0].y = fromY;
\r
9184 highlightInfo.sq[1].x = toX;
\r
9185 highlightInfo.sq[1].y = toY;
\r
9191 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9192 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9196 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9198 premoveHighlightInfo.sq[0].x = fromX;
\r
9199 premoveHighlightInfo.sq[0].y = fromY;
\r
9200 premoveHighlightInfo.sq[1].x = toX;
\r
9201 premoveHighlightInfo.sq[1].y = toY;
\r
9205 ClearPremoveHighlights()
\r
9207 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9208 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9212 ShutDownFrontEnd()
\r
9214 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9215 DeleteClipboardTempFiles();
\r
9221 if (IsIconic(hwndMain))
\r
9222 ShowWindow(hwndMain, SW_RESTORE);
\r
9224 SetActiveWindow(hwndMain);
\r
9228 * Prototypes for animation support routines
\r
9230 static void ScreenSquare(int column, int row, POINT * pt);
\r
9231 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9232 POINT frames[], int * nFrames);
\r
9236 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9237 { // [HGM] atomic: animate blast wave
\r
9239 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9240 explodeInfo.fromX = fromX;
\r
9241 explodeInfo.fromY = fromY;
\r
9242 explodeInfo.toX = toX;
\r
9243 explodeInfo.toY = toY;
\r
9244 for(i=1; i<nFrames; i++) {
\r
9245 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9246 DrawPosition(FALSE, NULL);
\r
9247 Sleep(appData.animSpeed);
\r
9249 explodeInfo.radius = 0;
\r
9250 DrawPosition(TRUE, NULL);
\r
9256 AnimateMove(board, fromX, fromY, toX, toY)
\r
9263 ChessSquare piece;
\r
9264 POINT start, finish, mid;
\r
9265 POINT frames[kFactor * 2 + 1];
\r
9268 if (!appData.animate) return;
\r
9269 if (doingSizing) return;
\r
9270 if (fromY < 0 || fromX < 0) return;
\r
9271 piece = board[fromY][fromX];
\r
9272 if (piece >= EmptySquare) return;
\r
9274 ScreenSquare(fromX, fromY, &start);
\r
9275 ScreenSquare(toX, toY, &finish);
\r
9277 /* All pieces except knights move in straight line */
\r
9278 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9279 mid.x = start.x + (finish.x - start.x) / 2;
\r
9280 mid.y = start.y + (finish.y - start.y) / 2;
\r
9282 /* Knight: make diagonal movement then straight */
\r
9283 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9284 mid.x = start.x + (finish.x - start.x) / 2;
\r
9288 mid.y = start.y + (finish.y - start.y) / 2;
\r
9292 /* Don't use as many frames for very short moves */
\r
9293 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9294 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9296 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9298 animInfo.from.x = fromX;
\r
9299 animInfo.from.y = fromY;
\r
9300 animInfo.to.x = toX;
\r
9301 animInfo.to.y = toY;
\r
9302 animInfo.lastpos = start;
\r
9303 animInfo.piece = piece;
\r
9304 for (n = 0; n < nFrames; n++) {
\r
9305 animInfo.pos = frames[n];
\r
9306 DrawPosition(FALSE, NULL);
\r
9307 animInfo.lastpos = animInfo.pos;
\r
9308 Sleep(appData.animSpeed);
\r
9310 animInfo.pos = finish;
\r
9311 DrawPosition(FALSE, NULL);
\r
9312 animInfo.piece = EmptySquare;
\r
9313 if(gameInfo.variant == VariantAtomic &&
\r
9314 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9315 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9318 /* Convert board position to corner of screen rect and color */
\r
9321 ScreenSquare(column, row, pt)
\r
9322 int column; int row; POINT * pt;
\r
9325 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9326 pt->y = lineGap + row * (squareSize + lineGap);
\r
9328 pt->x = lineGap + column * (squareSize + lineGap);
\r
9329 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9333 /* Generate a series of frame coords from start->mid->finish.
\r
9334 The movement rate doubles until the half way point is
\r
9335 reached, then halves back down to the final destination,
\r
9336 which gives a nice slow in/out effect. The algorithmn
\r
9337 may seem to generate too many intermediates for short
\r
9338 moves, but remember that the purpose is to attract the
\r
9339 viewers attention to the piece about to be moved and
\r
9340 then to where it ends up. Too few frames would be less
\r
9344 Tween(start, mid, finish, factor, frames, nFrames)
\r
9345 POINT * start; POINT * mid;
\r
9346 POINT * finish; int factor;
\r
9347 POINT frames[]; int * nFrames;
\r
9349 int n, fraction = 1, count = 0;
\r
9351 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9352 for (n = 0; n < factor; n++)
\r
9354 for (n = 0; n < factor; n++) {
\r
9355 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9356 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9358 fraction = fraction / 2;
\r
9362 frames[count] = *mid;
\r
9365 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9367 for (n = 0; n < factor; n++) {
\r
9368 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9369 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9371 fraction = fraction * 2;
\r
9377 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9379 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9381 EvalGraphSet( first, last, current, pvInfoList );
\r