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, 2010 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 int squareSize, lineGap, minorSize;
\r
163 static int winW, winH;
\r
164 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo
\r
165 static int logoHeight = 0;
\r
166 static char messageText[MESSAGE_TEXT_MAX];
\r
167 static int clockTimerEvent = 0;
\r
168 static int loadGameTimerEvent = 0;
\r
169 static int analysisTimerEvent = 0;
\r
170 static DelayedEventCallback delayedTimerCallback;
\r
171 static int delayedTimerEvent = 0;
\r
172 static int buttonCount = 2;
\r
173 char *icsTextMenuString;
\r
175 char *firstChessProgramNames;
\r
176 char *secondChessProgramNames;
\r
178 #define PALETTESIZE 256
\r
180 HINSTANCE hInst; /* current instance */
\r
181 Boolean alwaysOnTop = FALSE;
\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
184 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
186 ColorClass currentColorClass;
\r
188 HWND hCommPort = NULL; /* currently open comm port */
\r
189 static HWND hwndPause; /* pause button */
\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */
\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
192 blackSquareBrush, /* [HGM] for band between board and holdings */
\r
193 explodeBrush, /* [HGM] atomic */
\r
194 markerBrush, /* [HGM] markers */
\r
195 whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;
\r
196 static POINT gridEndpoints[(BOARD_RANKS + BOARD_FILES + 2) * 2];
\r
197 static DWORD gridVertexCounts[BOARD_RANKS + BOARD_FILES + 2];
\r
198 static HPEN gridPen = NULL;
\r
199 static HPEN highlightPen = NULL;
\r
200 static HPEN premovePen = NULL;
\r
201 static NPLOGPALETTE pLogPal;
\r
202 static BOOL paletteChanged = FALSE;
\r
203 static HICON iconWhite, iconBlack, iconCurrent;
\r
204 static int doingSizing = FALSE;
\r
205 static int lastSizing = 0;
\r
206 static int prevStderrPort;
\r
207 static HBITMAP userLogo;
\r
209 static HBITMAP liteBackTexture = NULL;
\r
210 static HBITMAP darkBackTexture = NULL;
\r
211 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
212 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;
\r
213 static int backTextureSquareSize = 0;
\r
214 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOARD_FILES];
\r
216 #if __GNUC__ && !defined(_winmajor)
\r
217 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
219 #if defined(_winmajor)
\r
220 #define oldDialog (_winmajor < 4)
\r
222 #define oldDialog 0
\r
232 int cliWidth, cliHeight;
\r
235 SizeInfo sizeInfo[] =
\r
237 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
238 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
239 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
240 { "petite", 33, 1, 1, 1, 0, 0 },
\r
241 { "slim", 37, 2, 1, 0, 0, 0 },
\r
242 { "small", 40, 2, 1, 0, 0, 0 },
\r
243 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
244 { "middling", 49, 2, 0, 0, 0, 0 },
\r
245 { "average", 54, 2, 0, 0, 0, 0 },
\r
246 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
247 { "medium", 64, 3, 0, 0, 0, 0 },
\r
248 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
249 { "large", 80, 3, 0, 0, 0, 0 },
\r
250 { "big", 87, 3, 0, 0, 0, 0 },
\r
251 { "huge", 95, 3, 0, 0, 0, 0 },
\r
252 { "giant", 108, 3, 0, 0, 0, 0 },
\r
253 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
254 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
255 { NULL, 0, 0, 0, 0, 0, 0 }
\r
258 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}
\r
259 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
261 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },
\r
262 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },
\r
263 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },
\r
264 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },
\r
265 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },
\r
266 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },
\r
267 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },
\r
268 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },
\r
269 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },
\r
270 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },
\r
271 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },
\r
272 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },
\r
273 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },
\r
274 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },
\r
275 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },
\r
276 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },
\r
277 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },
\r
278 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },
\r
281 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
290 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
291 #define N_BUTTONS 5
\r
293 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
295 {"<<", IDM_ToStart, NULL, NULL},
\r
296 {"<", IDM_Backward, NULL, NULL},
\r
297 {"P", IDM_Pause, NULL, NULL},
\r
298 {">", IDM_Forward, NULL, NULL},
\r
299 {">>", IDM_ToEnd, NULL, NULL},
\r
302 int tinyLayout = 0, smallLayout = 0;
\r
303 #define MENU_BAR_ITEMS 7
\r
304 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
305 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
306 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
310 MySound sounds[(int)NSoundClasses];
\r
311 MyTextAttribs textAttribs[(int)NColorClasses];
\r
313 MyColorizeAttribs colorizeAttribs[] = {
\r
314 { (COLORREF)0, 0, "Shout Text" },
\r
315 { (COLORREF)0, 0, "SShout/CShout" },
\r
316 { (COLORREF)0, 0, "Channel 1 Text" },
\r
317 { (COLORREF)0, 0, "Channel Text" },
\r
318 { (COLORREF)0, 0, "Kibitz Text" },
\r
319 { (COLORREF)0, 0, "Tell Text" },
\r
320 { (COLORREF)0, 0, "Challenge Text" },
\r
321 { (COLORREF)0, 0, "Request Text" },
\r
322 { (COLORREF)0, 0, "Seek Text" },
\r
323 { (COLORREF)0, 0, "Normal Text" },
\r
324 { (COLORREF)0, 0, "None" }
\r
329 static char *commentTitle;
\r
330 static char *commentText;
\r
331 static int commentIndex;
\r
332 static Boolean editComment = FALSE;
\r
335 char errorTitle[MSG_SIZ];
\r
336 char errorMessage[2*MSG_SIZ];
\r
337 HWND errorDialog = NULL;
\r
338 BOOLEAN moveErrorMessageUp = FALSE;
\r
339 BOOLEAN consoleEcho = TRUE;
\r
340 CHARFORMAT consoleCF;
\r
341 COLORREF consoleBackgroundColor;
\r
343 char *programVersion;
\r
349 typedef int CPKind;
\r
358 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
361 #define INPUT_SOURCE_BUF_SIZE 4096
\r
363 typedef struct _InputSource {
\r
370 char buf[INPUT_SOURCE_BUF_SIZE];
\r
374 InputCallback func;
\r
375 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
379 InputSource *consoleInputSource;
\r
384 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
385 VOID ConsoleCreate();
\r
387 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
388 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
389 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
390 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
392 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
393 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
394 void ParseIcsTextMenu(char *icsTextMenuString);
\r
395 VOID PopUpMoveDialog(char firstchar);
\r
396 VOID PopUpNameDialog(char firstchar);
\r
397 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
401 int GameListOptions();
\r
403 int dummy; // [HGM] for obsolete args
\r
405 HWND hwndMain = NULL; /* root window*/
\r
406 HWND hwndConsole = NULL;
\r
407 HWND commentDialog = NULL;
\r
408 HWND moveHistoryDialog = NULL;
\r
409 HWND evalGraphDialog = NULL;
\r
410 HWND engineOutputDialog = NULL;
\r
411 HWND gameListDialog = NULL;
\r
412 HWND editTagsDialog = NULL;
\r
414 int commentUp = FALSE;
\r
416 WindowPlacement wpMain;
\r
417 WindowPlacement wpConsole;
\r
418 WindowPlacement wpComment;
\r
419 WindowPlacement wpMoveHistory;
\r
420 WindowPlacement wpEvalGraph;
\r
421 WindowPlacement wpEngineOutput;
\r
422 WindowPlacement wpGameList;
\r
423 WindowPlacement wpTags;
\r
425 VOID EngineOptionsPopup(); // [HGM] settings
\r
427 VOID GothicPopUp(char *title, VariantClass variant);
\r
429 * Setting "frozen" should disable all user input other than deleting
\r
430 * the window. We do this while engines are initializing themselves.
\r
432 static int frozen = 0;
\r
433 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
439 if (frozen) return;
\r
441 hmenu = GetMenu(hwndMain);
\r
442 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
443 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
445 DrawMenuBar(hwndMain);
\r
448 /* Undo a FreezeUI */
\r
454 if (!frozen) return;
\r
456 hmenu = GetMenu(hwndMain);
\r
457 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
458 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
460 DrawMenuBar(hwndMain);
\r
463 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them
\r
465 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */
\r
471 #define JAWS_ALT_INTERCEPT
\r
472 #define JAWS_KB_NAVIGATION
\r
473 #define JAWS_MENU_ITEMS
\r
474 #define JAWS_SILENCE
\r
475 #define JAWS_REPLAY
\r
477 #define JAWS_COPYRIGHT
\r
478 #define JAWS_DELETE(X) X
\r
479 #define SAYMACHINEMOVE()
\r
483 /*---------------------------------------------------------------------------*\
\r
487 \*---------------------------------------------------------------------------*/
\r
490 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
491 LPSTR lpCmdLine, int nCmdShow)
\r
494 HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;
\r
495 // INITCOMMONCONTROLSEX ex;
\r
499 LoadLibrary("RICHED32.DLL");
\r
500 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
502 if (!InitApplication(hInstance)) {
\r
505 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
511 // InitCommonControlsEx(&ex);
\r
512 InitCommonControls();
\r
514 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
515 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
516 hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */
\r
518 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
520 while (GetMessage(&msg, /* message structure */
\r
521 NULL, /* handle of window receiving the message */
\r
522 0, /* lowest message to examine */
\r
523 0)) /* highest message to examine */
\r
526 if(msg.message == WM_CHAR && msg.wParam == '\t') {
\r
527 // [HGM] navigate: switch between all windows with tab
\r
528 HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;
\r
529 int i, currentElement = 0;
\r
531 // first determine what element of the chain we come from (if any)
\r
532 if(appData.icsActive) {
\r
533 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
534 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
536 if(engineOutputDialog && EngineOutputIsUp()) {
\r
537 e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);
\r
538 e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);
\r
540 if(moveHistoryDialog && MoveHistoryIsUp()) {
\r
541 mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);
\r
543 if(msg.hwnd == hwndMain) currentElement = 7 ; else
\r
544 if(msg.hwnd == engineOutputDialog) currentElement = 2; else
\r
545 if(msg.hwnd == e1) currentElement = 2; else
\r
546 if(msg.hwnd == e2) currentElement = 3; else
\r
547 if(msg.hwnd == moveHistoryDialog) currentElement = 4; else
\r
548 if(msg.hwnd == mh) currentElement = 4; else
\r
549 if(msg.hwnd == evalGraphDialog) currentElement = 6; else
\r
550 if(msg.hwnd == hText) currentElement = 5; else
\r
551 if(msg.hwnd == hInput) currentElement = 6; else
\r
552 for (i = 0; i < N_BUTTONS; i++) {
\r
553 if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }
\r
556 // determine where to go to
\r
557 if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;
\r
559 currentElement = (currentElement + direction) % 7;
\r
560 switch(currentElement) {
\r
562 h = hwndMain; break; // passing this case always makes the loop exit
\r
564 h = buttonDesc[0].hwnd; break; // could be NULL
\r
566 if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows
\r
569 if(!EngineOutputIsUp()) continue;
\r
572 if(!MoveHistoryIsUp()) continue;
\r
574 // case 6: // input to eval graph does not seem to get here!
\r
575 // if(!EvalGraphIsUp()) continue;
\r
576 // h = evalGraphDialog; break;
\r
578 if(!appData.icsActive) continue;
\r
582 if(!appData.icsActive) continue;
\r
588 if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
589 if(currentElement < 5 && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); // all open together
\r
592 continue; // this message now has been processed
\r
596 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
597 !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&
\r
598 !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&
\r
599 !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&
\r
600 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
601 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
602 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
603 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL
\r
604 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&
\r
605 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
606 int done = 0, i; // [HGM] chat: dispatch cat-box messages
\r
607 for(i=0; i<MAX_CHAT; i++)
\r
608 if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {
\r
611 if(done) continue; // [HGM] chat: end patch
\r
612 TranslateMessage(&msg); /* Translates virtual key codes */
\r
613 DispatchMessage(&msg); /* Dispatches message to window */
\r
618 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
621 /*---------------------------------------------------------------------------*\
\r
623 * Initialization functions
\r
625 \*---------------------------------------------------------------------------*/
\r
629 { // update user logo if necessary
\r
630 static char oldUserName[MSG_SIZ], *curName;
\r
632 if(appData.autoLogo) {
\r
633 curName = UserName();
\r
634 if(strcmp(curName, oldUserName)) {
\r
635 sprintf(oldUserName, "logos\\%s.bmp", curName);
\r
636 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
637 strcpy(oldUserName, curName);
\r
643 InitApplication(HINSTANCE hInstance)
\r
647 /* Fill in window class structure with parameters that describe the */
\r
650 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
651 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
652 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
653 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
654 wc.hInstance = hInstance; /* Owner of this class */
\r
655 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
656 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
657 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
658 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
659 wc.lpszClassName = szAppName; /* Name to register as */
\r
661 /* Register the window class and return success/failure code. */
\r
662 if (!RegisterClass(&wc)) return FALSE;
\r
664 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
665 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
667 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
668 wc.hInstance = hInstance;
\r
669 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
670 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
671 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
672 wc.lpszMenuName = NULL;
\r
673 wc.lpszClassName = szConsoleName;
\r
675 if (!RegisterClass(&wc)) return FALSE;
\r
680 /* Set by InitInstance, used by EnsureOnScreen */
\r
681 int screenHeight, screenWidth;
\r
684 EnsureOnScreen(int *x, int *y, int minX, int minY)
\r
686 // int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
\r
687 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
688 if (*x > screenWidth - 32) *x = 0;
\r
689 if (*y > screenHeight - 32) *y = 0;
\r
690 if (*x < minX) *x = minX;
\r
691 if (*y < minY) *y = minY;
\r
695 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
697 HWND hwnd; /* Main window handle. */
\r
699 WINDOWPLACEMENT wp;
\r
702 hInst = hInstance; /* Store instance handle in our global variable */
\r
703 programName = szAppName;
\r
705 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
706 *filepart = NULLCHAR;
\r
708 GetCurrentDirectory(MSG_SIZ, installDir);
\r
710 gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise
\r
711 screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData
\r
712 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
713 /* xboard, and older WinBoards, controlled the move sound with the
\r
714 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
715 always turn the option on (so that the backend will call us),
\r
716 then let the user turn the sound off by setting it to silence if
\r
717 desired. To accommodate old winboard.ini files saved by old
\r
718 versions of WinBoard, we also turn off the sound if the option
\r
719 was initially set to false. [HGM] taken out of InitAppData */
\r
720 if (!appData.ringBellAfterMoves) {
\r
721 sounds[(int)SoundMove].name = strdup("");
\r
722 appData.ringBellAfterMoves = TRUE;
\r
724 if (appData.debugMode) {
\r
725 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
726 setbuf(debugFP, NULL);
\r
731 // InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()
\r
732 // InitEngineUCI( installDir, &second );
\r
734 /* Create a main window for this application instance. */
\r
735 hwnd = CreateWindow(szAppName, szTitle,
\r
736 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
737 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
738 NULL, NULL, hInstance, NULL);
\r
741 /* If window could not be created, return "failure" */
\r
746 /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */
\r
747 if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {
\r
748 first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
750 if (first.programLogo == NULL && appData.debugMode) {
\r
751 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );
\r
753 } else if(appData.autoLogo) {
\r
754 if(appData.firstDirectory && appData.firstDirectory[0]) {
\r
756 sprintf(buf, "%s/logo.bmp", appData.firstDirectory);
\r
757 first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
761 if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {
\r
762 second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
764 if (second.programLogo == NULL && appData.debugMode) {
\r
765 fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );
\r
767 } else if(appData.autoLogo) {
\r
769 if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS
\r
770 sprintf(buf, "logos\\%s.bmp", appData.icsHost);
\r
771 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
773 if(appData.secondDirectory && appData.secondDirectory[0]) {
\r
774 sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);
\r
775 second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
781 iconWhite = LoadIcon(hInstance, "icon_white");
\r
782 iconBlack = LoadIcon(hInstance, "icon_black");
\r
783 iconCurrent = iconWhite;
\r
784 InitDrawingColors();
\r
785 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
786 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
787 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
788 /* Compute window size for each board size, and use the largest
\r
789 size that fits on this screen as the default. */
\r
790 InitDrawingSizes((BoardSize)(ibs+1000), 0);
\r
791 if (boardSize == (BoardSize)-1 &&
\r
792 winH <= screenHeight
\r
793 - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10
\r
794 && winW <= screenWidth) {
\r
795 boardSize = (BoardSize)ibs;
\r
799 InitDrawingSizes(boardSize, 0);
\r
801 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
803 /* [AS] Load textures if specified */
\r
804 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
806 if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {
\r
807 liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
808 liteBackTextureMode = appData.liteBackTextureMode;
\r
810 if (liteBackTexture == NULL && appData.debugMode) {
\r
811 fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );
\r
815 if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {
\r
816 darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
\r
817 darkBackTextureMode = appData.darkBackTextureMode;
\r
819 if (darkBackTexture == NULL && appData.debugMode) {
\r
820 fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );
\r
824 mysrandom( (unsigned) time(NULL) );
\r
826 /* [AS] Restore layout */
\r
827 if( wpMoveHistory.visible ) {
\r
828 MoveHistoryPopUp();
\r
831 if( wpEvalGraph.visible ) {
\r
835 if( wpEngineOutput.visible ) {
\r
836 EngineOutputPopUp();
\r
841 /* Make the window visible; update its client area; and return "success" */
\r
842 EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);
\r
843 wp.length = sizeof(WINDOWPLACEMENT);
\r
845 wp.showCmd = nCmdShow;
\r
846 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
847 wp.rcNormalPosition.left = wpMain.x;
\r
848 wp.rcNormalPosition.right = wpMain.x + wpMain.width;
\r
849 wp.rcNormalPosition.top = wpMain.y;
\r
850 wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;
\r
851 SetWindowPlacement(hwndMain, &wp);
\r
853 if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
854 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
858 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
859 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
861 ShowWindow(hwndConsole, nCmdShow);
\r
863 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
864 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
873 HMENU hmenu = GetMenu(hwndMain);
\r
875 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
876 MF_BYCOMMAND|((appData.icsActive &&
\r
877 *appData.icsCommPort != NULLCHAR) ?
\r
878 MF_ENABLED : MF_GRAYED));
\r
879 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
880 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
881 MF_CHECKED : MF_UNCHECKED));
\r
884 //---------------------------------------------------------------------------------------------------------
\r
886 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
887 #define XBOARD FALSE
\r
889 #define OPTCHAR "/"
\r
890 #define SEPCHAR "="
\r
894 // front-end part of option handling
\r
897 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
899 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
900 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
903 lf->lfEscapement = 0;
\r
904 lf->lfOrientation = 0;
\r
905 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
906 lf->lfItalic = mfp->italic;
\r
907 lf->lfUnderline = mfp->underline;
\r
908 lf->lfStrikeOut = mfp->strikeout;
\r
909 lf->lfCharSet = mfp->charset;
\r
910 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
911 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
912 lf->lfQuality = DEFAULT_QUALITY;
\r
913 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
914 strcpy(lf->lfFaceName, mfp->faceName);
\r
918 CreateFontInMF(MyFont *mf)
\r
920 LFfromMFP(&mf->lf, &mf->mfp);
\r
921 if (mf->hf) DeleteObject(mf->hf);
\r
922 mf->hf = CreateFontIndirect(&mf->lf);
\r
925 // [HGM] This platform-dependent table provides the location for storing the color info
\r
927 colorVariable[] = {
\r
932 &highlightSquareColor,
\r
933 &premoveHighlightColor,
\r
935 &consoleBackgroundColor,
\r
936 &appData.fontForeColorWhite,
\r
937 &appData.fontBackColorWhite,
\r
938 &appData.fontForeColorBlack,
\r
939 &appData.fontBackColorBlack,
\r
940 &appData.evalHistColorWhite,
\r
941 &appData.evalHistColorBlack,
\r
942 &appData.highlightArrowColor,
\r
945 /* Command line font name parser. NULL name means do nothing.
\r
946 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
947 For backward compatibility, syntax without the colon is also
\r
948 accepted, but font names with digits in them won't work in that case.
\r
951 ParseFontName(char *name, MyFontParams *mfp)
\r
954 if (name == NULL) return;
\r
956 q = strchr(p, ':');
\r
958 if (q - p >= sizeof(mfp->faceName))
\r
959 ExitArgError("Font name too long:", name);
\r
960 memcpy(mfp->faceName, p, q - p);
\r
961 mfp->faceName[q - p] = NULLCHAR;
\r
965 while (*p && !isdigit(*p)) {
\r
967 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
968 ExitArgError("Font name too long:", name);
\r
970 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
973 if (!*p) ExitArgError("Font point size missing:", name);
\r
974 mfp->pointSize = (float) atof(p);
\r
975 mfp->bold = (strchr(p, 'b') != NULL);
\r
976 mfp->italic = (strchr(p, 'i') != NULL);
\r
977 mfp->underline = (strchr(p, 'u') != NULL);
\r
978 mfp->strikeout = (strchr(p, 's') != NULL);
\r
979 mfp->charset = DEFAULT_CHARSET;
\r
980 q = strchr(p, 'c');
\r
982 mfp->charset = (BYTE) atoi(q+1);
\r
986 ParseFont(char *name, int number)
\r
987 { // wrapper to shield back-end from 'font'
\r
988 ParseFontName(name, &font[boardSize][number]->mfp);
\r
993 { // in WB we have a 2D array of fonts; this initializes their description
\r
995 /* Point font array elements to structures and
\r
996 parse default font names */
\r
997 for (i=0; i<NUM_FONTS; i++) {
\r
998 for (j=0; j<NUM_SIZES; j++) {
\r
999 font[j][i] = &fontRec[j][i];
\r
1000 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1007 { // here we create the actual fonts from the selected descriptions
\r
1009 for (i=0; i<NUM_FONTS; i++) {
\r
1010 for (j=0; j<NUM_SIZES; j++) {
\r
1011 CreateFontInMF(font[j][i]);
\r
1015 /* Color name parser.
\r
1016 X version accepts X color names, but this one
\r
1017 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1019 ParseColorName(char *name)
\r
1021 int red, green, blue, count;
\r
1022 char buf[MSG_SIZ];
\r
1024 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1026 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1027 &red, &green, &blue);
\r
1030 sprintf(buf, "Can't parse color name %s", name);
\r
1031 DisplayError(buf, 0);
\r
1032 return RGB(0, 0, 0);
\r
1034 return PALETTERGB(red, green, blue);
\r
1038 ParseColor(int n, char *name)
\r
1039 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1040 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1044 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1046 char *e = argValue;
\r
1050 if (*e == 'b') eff |= CFE_BOLD;
\r
1051 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1052 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1053 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1054 else if (*e == '#' || isdigit(*e)) break;
\r
1058 *color = ParseColorName(e);
\r
1062 ParseTextAttribs(ColorClass cc, char *s)
\r
1063 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1064 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1065 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1069 ParseBoardSize(void *addr, char *name)
\r
1070 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1071 BoardSize bs = SizeTiny;
\r
1072 while (sizeInfo[bs].name != NULL) {
\r
1073 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1074 *(BoardSize *)addr = bs;
\r
1079 ExitArgError("Unrecognized board size value", name);
\r
1084 { // [HGM] import name from appData first
\r
1087 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1088 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1089 textAttribs[cc].sound.data = NULL;
\r
1090 MyLoadSound(&textAttribs[cc].sound);
\r
1092 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1093 textAttribs[cc].sound.name = strdup("");
\r
1094 textAttribs[cc].sound.data = NULL;
\r
1096 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1097 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1098 sounds[sc].data = NULL;
\r
1099 MyLoadSound(&sounds[sc]);
\r
1104 SetCommPortDefaults()
\r
1106 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1107 dcb.DCBlength = sizeof(DCB);
\r
1108 dcb.BaudRate = 9600;
\r
1109 dcb.fBinary = TRUE;
\r
1110 dcb.fParity = FALSE;
\r
1111 dcb.fOutxCtsFlow = FALSE;
\r
1112 dcb.fOutxDsrFlow = FALSE;
\r
1113 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1114 dcb.fDsrSensitivity = FALSE;
\r
1115 dcb.fTXContinueOnXoff = TRUE;
\r
1116 dcb.fOutX = FALSE;
\r
1118 dcb.fNull = FALSE;
\r
1119 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1120 dcb.fAbortOnError = FALSE;
\r
1122 dcb.Parity = SPACEPARITY;
\r
1123 dcb.StopBits = ONESTOPBIT;
\r
1126 // [HGM] args: these three cases taken out to stay in front-end
\r
1128 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1129 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1130 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1131 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1133 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1134 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1135 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1136 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1137 ad->argName, mfp->faceName, mfp->pointSize,
\r
1138 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1139 mfp->bold ? "b" : "",
\r
1140 mfp->italic ? "i" : "",
\r
1141 mfp->underline ? "u" : "",
\r
1142 mfp->strikeout ? "s" : "",
\r
1143 (int)mfp->charset);
\r
1149 { // [HGM] copy the names from the internal WB variables to appData
\r
1152 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1153 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1154 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1155 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1159 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1160 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1161 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1162 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1163 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1164 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1165 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1166 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1167 (ta->effects) ? " " : "",
\r
1168 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1172 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1173 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1174 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1175 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1176 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1180 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1181 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1182 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1186 ParseCommPortSettings(char *s)
\r
1187 { // wrapper to keep dcb from back-end
\r
1188 ParseCommSettings(s, &dcb);
\r
1193 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1194 GetActualPlacement(hwndMain, &wpMain);
\r
1195 GetActualPlacement(hwndConsole, &wpConsole);
\r
1196 GetActualPlacement(commentDialog, &wpComment);
\r
1197 GetActualPlacement(editTagsDialog, &wpTags);
\r
1198 GetActualPlacement(gameListDialog, &wpGameList);
\r
1199 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1200 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1201 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1205 PrintCommPortSettings(FILE *f, char *name)
\r
1206 { // wrapper to shield back-end from DCB
\r
1207 PrintCommSettings(f, name, &dcb);
\r
1211 MySearchPath(char *installDir, char *name, char *fullname)
\r
1214 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1218 MyGetFullPathName(char *name, char *fullname)
\r
1221 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1226 { // [HGM] args: allows testing if main window is realized from back-end
\r
1227 return hwndMain != NULL;
\r
1231 PopUpStartupDialog()
\r
1235 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1236 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1237 FreeProcInstance(lpProc);
\r
1240 /*---------------------------------------------------------------------------*\
\r
1242 * GDI board drawing routines
\r
1244 \*---------------------------------------------------------------------------*/
\r
1246 /* [AS] Draw square using background texture */
\r
1247 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1252 return; /* Should never happen! */
\r
1255 SetGraphicsMode( dst, GM_ADVANCED );
\r
1262 /* X reflection */
\r
1267 x.eDx = (FLOAT) dw + dx - 1;
\r
1270 SetWorldTransform( dst, &x );
\r
1273 /* Y reflection */
\r
1279 x.eDy = (FLOAT) dh + dy - 1;
\r
1281 SetWorldTransform( dst, &x );
\r
1289 x.eDx = (FLOAT) dx;
\r
1290 x.eDy = (FLOAT) dy;
\r
1293 SetWorldTransform( dst, &x );
\r
1297 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1305 SetWorldTransform( dst, &x );
\r
1307 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1310 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1312 PM_WP = (int) WhitePawn,
\r
1313 PM_WN = (int) WhiteKnight,
\r
1314 PM_WB = (int) WhiteBishop,
\r
1315 PM_WR = (int) WhiteRook,
\r
1316 PM_WQ = (int) WhiteQueen,
\r
1317 PM_WF = (int) WhiteFerz,
\r
1318 PM_WW = (int) WhiteWazir,
\r
1319 PM_WE = (int) WhiteAlfil,
\r
1320 PM_WM = (int) WhiteMan,
\r
1321 PM_WO = (int) WhiteCannon,
\r
1322 PM_WU = (int) WhiteUnicorn,
\r
1323 PM_WH = (int) WhiteNightrider,
\r
1324 PM_WA = (int) WhiteAngel,
\r
1325 PM_WC = (int) WhiteMarshall,
\r
1326 PM_WAB = (int) WhiteCardinal,
\r
1327 PM_WD = (int) WhiteDragon,
\r
1328 PM_WL = (int) WhiteLance,
\r
1329 PM_WS = (int) WhiteCobra,
\r
1330 PM_WV = (int) WhiteFalcon,
\r
1331 PM_WSG = (int) WhiteSilver,
\r
1332 PM_WG = (int) WhiteGrasshopper,
\r
1333 PM_WK = (int) WhiteKing,
\r
1334 PM_BP = (int) BlackPawn,
\r
1335 PM_BN = (int) BlackKnight,
\r
1336 PM_BB = (int) BlackBishop,
\r
1337 PM_BR = (int) BlackRook,
\r
1338 PM_BQ = (int) BlackQueen,
\r
1339 PM_BF = (int) BlackFerz,
\r
1340 PM_BW = (int) BlackWazir,
\r
1341 PM_BE = (int) BlackAlfil,
\r
1342 PM_BM = (int) BlackMan,
\r
1343 PM_BO = (int) BlackCannon,
\r
1344 PM_BU = (int) BlackUnicorn,
\r
1345 PM_BH = (int) BlackNightrider,
\r
1346 PM_BA = (int) BlackAngel,
\r
1347 PM_BC = (int) BlackMarshall,
\r
1348 PM_BG = (int) BlackGrasshopper,
\r
1349 PM_BAB = (int) BlackCardinal,
\r
1350 PM_BD = (int) BlackDragon,
\r
1351 PM_BL = (int) BlackLance,
\r
1352 PM_BS = (int) BlackCobra,
\r
1353 PM_BV = (int) BlackFalcon,
\r
1354 PM_BSG = (int) BlackSilver,
\r
1355 PM_BK = (int) BlackKing
\r
1358 static HFONT hPieceFont = NULL;
\r
1359 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1360 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1361 static int fontBitmapSquareSize = 0;
\r
1362 static char pieceToFontChar[(int) EmptySquare] =
\r
1363 { 'p', 'n', 'b', 'r', 'q',
\r
1364 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1365 'k', 'o', 'm', 'v', 't', 'w',
\r
1366 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1369 extern BOOL SetCharTable( char *table, const char * map );
\r
1370 /* [HGM] moved to backend.c */
\r
1372 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1375 BYTE r1 = GetRValue( color );
\r
1376 BYTE g1 = GetGValue( color );
\r
1377 BYTE b1 = GetBValue( color );
\r
1383 /* Create a uniform background first */
\r
1384 hbrush = CreateSolidBrush( color );
\r
1385 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1386 FillRect( hdc, &rc, hbrush );
\r
1387 DeleteObject( hbrush );
\r
1390 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1391 int steps = squareSize / 2;
\r
1394 for( i=0; i<steps; i++ ) {
\r
1395 BYTE r = r1 - (r1-r2) * i / steps;
\r
1396 BYTE g = g1 - (g1-g2) * i / steps;
\r
1397 BYTE b = b1 - (b1-b2) * i / steps;
\r
1399 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1400 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1401 FillRect( hdc, &rc, hbrush );
\r
1402 DeleteObject(hbrush);
\r
1405 else if( mode == 2 ) {
\r
1406 /* Diagonal gradient, good more or less for every piece */
\r
1407 POINT triangle[3];
\r
1408 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1409 HBRUSH hbrush_old;
\r
1410 int steps = squareSize;
\r
1413 triangle[0].x = squareSize - steps;
\r
1414 triangle[0].y = squareSize;
\r
1415 triangle[1].x = squareSize;
\r
1416 triangle[1].y = squareSize;
\r
1417 triangle[2].x = squareSize;
\r
1418 triangle[2].y = squareSize - steps;
\r
1420 for( i=0; i<steps; i++ ) {
\r
1421 BYTE r = r1 - (r1-r2) * i / steps;
\r
1422 BYTE g = g1 - (g1-g2) * i / steps;
\r
1423 BYTE b = b1 - (b1-b2) * i / steps;
\r
1425 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1426 hbrush_old = SelectObject( hdc, hbrush );
\r
1427 Polygon( hdc, triangle, 3 );
\r
1428 SelectObject( hdc, hbrush_old );
\r
1429 DeleteObject(hbrush);
\r
1434 SelectObject( hdc, hpen );
\r
1439 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1440 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1441 piece: follow the steps as explained below.
\r
1443 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1447 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1451 int backColor = whitePieceColor;
\r
1452 int foreColor = blackPieceColor;
\r
1454 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1455 backColor = appData.fontBackColorWhite;
\r
1456 foreColor = appData.fontForeColorWhite;
\r
1458 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1459 backColor = appData.fontBackColorBlack;
\r
1460 foreColor = appData.fontForeColorBlack;
\r
1464 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1466 hbm_old = SelectObject( hdc, hbm );
\r
1470 rc.right = squareSize;
\r
1471 rc.bottom = squareSize;
\r
1473 /* Step 1: background is now black */
\r
1474 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1476 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1478 pt.x = (squareSize - sz.cx) / 2;
\r
1479 pt.y = (squareSize - sz.cy) / 2;
\r
1481 SetBkMode( hdc, TRANSPARENT );
\r
1482 SetTextColor( hdc, chroma );
\r
1483 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1484 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1486 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1487 /* Step 3: the area outside the piece is filled with white */
\r
1488 // FloodFill( hdc, 0, 0, chroma );
\r
1489 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1490 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1491 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1492 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1493 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1495 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1496 but if the start point is not inside the piece we're lost!
\r
1497 There should be a better way to do this... if we could create a region or path
\r
1498 from the fill operation we would be fine for example.
\r
1500 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1501 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1503 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1504 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1505 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1507 SelectObject( dc2, bm2 );
\r
1508 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1509 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1510 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1511 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1512 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1515 DeleteObject( bm2 );
\r
1518 SetTextColor( hdc, 0 );
\r
1520 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1521 draw the piece again in black for safety.
\r
1523 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1525 SelectObject( hdc, hbm_old );
\r
1527 if( hPieceMask[index] != NULL ) {
\r
1528 DeleteObject( hPieceMask[index] );
\r
1531 hPieceMask[index] = hbm;
\r
1534 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1536 SelectObject( hdc, hbm );
\r
1539 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1540 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1541 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1543 SelectObject( dc1, hPieceMask[index] );
\r
1544 SelectObject( dc2, bm2 );
\r
1545 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1546 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1549 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1550 the piece background and deletes (makes transparent) the rest.
\r
1551 Thanks to that mask, we are free to paint the background with the greates
\r
1552 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1553 We use this, to make gradients and give the pieces a "roundish" look.
\r
1555 SetPieceBackground( hdc, backColor, 2 );
\r
1556 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1560 DeleteObject( bm2 );
\r
1563 SetTextColor( hdc, foreColor );
\r
1564 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1566 SelectObject( hdc, hbm_old );
\r
1568 if( hPieceFace[index] != NULL ) {
\r
1569 DeleteObject( hPieceFace[index] );
\r
1572 hPieceFace[index] = hbm;
\r
1575 static int TranslatePieceToFontPiece( int piece )
\r
1605 case BlackMarshall:
\r
1609 case BlackNightrider:
\r
1615 case BlackUnicorn:
\r
1619 case BlackGrasshopper:
\r
1631 case BlackCardinal:
\r
1638 case WhiteMarshall:
\r
1642 case WhiteNightrider:
\r
1648 case WhiteUnicorn:
\r
1652 case WhiteGrasshopper:
\r
1664 case WhiteCardinal:
\r
1673 void CreatePiecesFromFont()
\r
1676 HDC hdc_window = NULL;
\r
1682 if( fontBitmapSquareSize < 0 ) {
\r
1683 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1687 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1688 fontBitmapSquareSize = -1;
\r
1692 if( fontBitmapSquareSize != squareSize ) {
\r
1693 hdc_window = GetDC( hwndMain );
\r
1694 hdc = CreateCompatibleDC( hdc_window );
\r
1696 if( hPieceFont != NULL ) {
\r
1697 DeleteObject( hPieceFont );
\r
1700 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1701 hPieceMask[i] = NULL;
\r
1702 hPieceFace[i] = NULL;
\r
1708 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1709 fontHeight = appData.fontPieceSize;
\r
1712 fontHeight = (fontHeight * squareSize) / 100;
\r
1714 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1716 lf.lfEscapement = 0;
\r
1717 lf.lfOrientation = 0;
\r
1718 lf.lfWeight = FW_NORMAL;
\r
1720 lf.lfUnderline = 0;
\r
1721 lf.lfStrikeOut = 0;
\r
1722 lf.lfCharSet = DEFAULT_CHARSET;
\r
1723 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1724 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1725 lf.lfQuality = PROOF_QUALITY;
\r
1726 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1727 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1728 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1730 hPieceFont = CreateFontIndirect( &lf );
\r
1732 if( hPieceFont == NULL ) {
\r
1733 fontBitmapSquareSize = -2;
\r
1736 /* Setup font-to-piece character table */
\r
1737 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1738 /* No (or wrong) global settings, try to detect the font */
\r
1739 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1741 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1743 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1744 /* DiagramTT* family */
\r
1745 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1747 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1748 /* Fairy symbols */
\r
1749 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1751 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1752 /* Good Companion (Some characters get warped as literal :-( */
\r
1753 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1754 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1755 SetCharTable(pieceToFontChar, s);
\r
1758 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1759 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1763 /* Create bitmaps */
\r
1764 hfont_old = SelectObject( hdc, hPieceFont );
\r
1765 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1766 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1767 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1769 SelectObject( hdc, hfont_old );
\r
1771 fontBitmapSquareSize = squareSize;
\r
1775 if( hdc != NULL ) {
\r
1779 if( hdc_window != NULL ) {
\r
1780 ReleaseDC( hwndMain, hdc_window );
\r
1785 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1789 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1790 if (gameInfo.event &&
\r
1791 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1792 strcmp(name, "k80s") == 0) {
\r
1793 strcpy(name, "tim");
\r
1795 return LoadBitmap(hinst, name);
\r
1799 /* Insert a color into the program's logical palette
\r
1800 structure. This code assumes the given color is
\r
1801 the result of the RGB or PALETTERGB macro, and it
\r
1802 knows how those macros work (which is documented).
\r
1805 InsertInPalette(COLORREF color)
\r
1807 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1809 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1810 DisplayFatalError("Too many colors", 0, 1);
\r
1811 pLogPal->palNumEntries--;
\r
1815 pe->peFlags = (char) 0;
\r
1816 pe->peRed = (char) (0xFF & color);
\r
1817 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1818 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1824 InitDrawingColors()
\r
1826 if (pLogPal == NULL) {
\r
1827 /* Allocate enough memory for a logical palette with
\r
1828 * PALETTESIZE entries and set the size and version fields
\r
1829 * of the logical palette structure.
\r
1831 pLogPal = (NPLOGPALETTE)
\r
1832 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1833 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1834 pLogPal->palVersion = 0x300;
\r
1836 pLogPal->palNumEntries = 0;
\r
1838 InsertInPalette(lightSquareColor);
\r
1839 InsertInPalette(darkSquareColor);
\r
1840 InsertInPalette(whitePieceColor);
\r
1841 InsertInPalette(blackPieceColor);
\r
1842 InsertInPalette(highlightSquareColor);
\r
1843 InsertInPalette(premoveHighlightColor);
\r
1845 /* create a logical color palette according the information
\r
1846 * in the LOGPALETTE structure.
\r
1848 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1850 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1851 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1852 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1853 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1854 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1855 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1856 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1857 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1858 /* [AS] Force rendering of the font-based pieces */
\r
1859 if( fontBitmapSquareSize > 0 ) {
\r
1860 fontBitmapSquareSize = 0;
\r
1866 BoardWidth(int boardSize, int n)
\r
1867 { /* [HGM] argument n added to allow different width and height */
\r
1868 int lineGap = sizeInfo[boardSize].lineGap;
\r
1870 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1871 lineGap = appData.overrideLineGap;
\r
1874 return (n + 1) * lineGap +
\r
1875 n * sizeInfo[boardSize].squareSize;
\r
1878 /* Respond to board resize by dragging edge */
\r
1880 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1882 BoardSize newSize = NUM_SIZES - 1;
\r
1883 static int recurse = 0;
\r
1884 if (IsIconic(hwndMain)) return;
\r
1885 if (recurse > 0) return;
\r
1887 while (newSize > 0) {
\r
1888 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1889 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1890 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1893 boardSize = newSize;
\r
1894 InitDrawingSizes(boardSize, flags);
\r
1901 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1903 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1904 ChessSquare piece;
\r
1905 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1907 SIZE clockSize, messageSize;
\r
1909 char buf[MSG_SIZ];
\r
1911 HMENU hmenu = GetMenu(hwndMain);
\r
1912 RECT crect, wrect, oldRect;
\r
1914 LOGBRUSH logbrush;
\r
1916 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1917 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1919 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1920 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1922 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1923 oldRect.top = wpMain.y;
\r
1924 oldRect.right = wpMain.x + wpMain.width;
\r
1925 oldRect.bottom = wpMain.y + wpMain.height;
\r
1927 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1928 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1929 squareSize = sizeInfo[boardSize].squareSize;
\r
1930 lineGap = sizeInfo[boardSize].lineGap;
\r
1931 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1933 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1934 lineGap = appData.overrideLineGap;
\r
1937 if (tinyLayout != oldTinyLayout) {
\r
1938 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1940 style &= ~WS_SYSMENU;
\r
1941 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1942 "&Minimize\tCtrl+F4");
\r
1944 style |= WS_SYSMENU;
\r
1945 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1947 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1949 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1950 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1951 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1953 DrawMenuBar(hwndMain);
\r
1956 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1957 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1959 /* Get text area sizes */
\r
1960 hdc = GetDC(hwndMain);
\r
1961 if (appData.clockMode) {
\r
1962 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1964 sprintf(buf, "White");
\r
1966 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1967 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1968 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1969 str = "We only care about the height here";
\r
1970 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1971 SelectObject(hdc, oldFont);
\r
1972 ReleaseDC(hwndMain, hdc);
\r
1974 /* Compute where everything goes */
\r
1975 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1976 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1977 logoHeight = 2*clockSize.cy;
\r
1978 leftLogoRect.left = OUTER_MARGIN;
\r
1979 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1980 leftLogoRect.top = OUTER_MARGIN;
\r
1981 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1983 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1984 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1985 rightLogoRect.top = OUTER_MARGIN;
\r
1986 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1989 whiteRect.left = leftLogoRect.right;
\r
1990 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
1991 whiteRect.top = OUTER_MARGIN;
\r
1992 whiteRect.bottom = whiteRect.top + logoHeight;
\r
1994 blackRect.right = rightLogoRect.left;
\r
1995 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
1996 blackRect.top = whiteRect.top;
\r
1997 blackRect.bottom = whiteRect.bottom;
\r
1999 whiteRect.left = OUTER_MARGIN;
\r
2000 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2001 whiteRect.top = OUTER_MARGIN;
\r
2002 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2004 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2005 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2006 blackRect.top = whiteRect.top;
\r
2007 blackRect.bottom = whiteRect.bottom;
\r
2009 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2012 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2013 if (appData.showButtonBar) {
\r
2014 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2015 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2017 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2019 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2020 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2022 boardRect.left = OUTER_MARGIN;
\r
2023 boardRect.right = boardRect.left + boardWidth;
\r
2024 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2025 boardRect.bottom = boardRect.top + boardHeight;
\r
2027 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2028 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2029 oldBoardSize = boardSize;
\r
2030 oldTinyLayout = tinyLayout;
\r
2031 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2032 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2033 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2034 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2035 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2036 wpMain.height = winH; // without disturbing window attachments
\r
2037 GetWindowRect(hwndMain, &wrect);
\r
2038 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2039 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2041 // [HGM] placement: let attached windows follow size change.
\r
2042 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2043 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2044 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2045 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2046 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2048 /* compensate if menu bar wrapped */
\r
2049 GetClientRect(hwndMain, &crect);
\r
2050 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2051 wpMain.height += offby;
\r
2053 case WMSZ_TOPLEFT:
\r
2054 SetWindowPos(hwndMain, NULL,
\r
2055 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2056 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2059 case WMSZ_TOPRIGHT:
\r
2061 SetWindowPos(hwndMain, NULL,
\r
2062 wrect.left, wrect.bottom - wpMain.height,
\r
2063 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2066 case WMSZ_BOTTOMLEFT:
\r
2068 SetWindowPos(hwndMain, NULL,
\r
2069 wrect.right - wpMain.width, wrect.top,
\r
2070 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2073 case WMSZ_BOTTOMRIGHT:
\r
2077 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2078 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2083 for (i = 0; i < N_BUTTONS; i++) {
\r
2084 if (buttonDesc[i].hwnd != NULL) {
\r
2085 DestroyWindow(buttonDesc[i].hwnd);
\r
2086 buttonDesc[i].hwnd = NULL;
\r
2088 if (appData.showButtonBar) {
\r
2089 buttonDesc[i].hwnd =
\r
2090 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2091 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2092 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2093 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2094 (HMENU) buttonDesc[i].id,
\r
2095 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2097 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2098 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2099 MAKELPARAM(FALSE, 0));
\r
2101 if (buttonDesc[i].id == IDM_Pause)
\r
2102 hwndPause = buttonDesc[i].hwnd;
\r
2103 buttonDesc[i].wndproc = (WNDPROC)
\r
2104 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2107 if (gridPen != NULL) DeleteObject(gridPen);
\r
2108 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2109 if (premovePen != NULL) DeleteObject(premovePen);
\r
2110 if (lineGap != 0) {
\r
2111 logbrush.lbStyle = BS_SOLID;
\r
2112 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2114 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2115 lineGap, &logbrush, 0, NULL);
\r
2116 logbrush.lbColor = highlightSquareColor;
\r
2118 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2119 lineGap, &logbrush, 0, NULL);
\r
2121 logbrush.lbColor = premoveHighlightColor;
\r
2123 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2124 lineGap, &logbrush, 0, NULL);
\r
2126 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2127 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2128 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2129 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2130 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2131 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2132 BOARD_WIDTH * (squareSize + lineGap);
\r
2133 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2135 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2136 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2137 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2138 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2139 lineGap / 2 + (i * (squareSize + lineGap));
\r
2140 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2141 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2142 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2146 /* [HGM] Licensing requirement */
\r
2148 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2151 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2153 GothicPopUp( "", VariantNormal);
\r
2156 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2158 /* Load piece bitmaps for this board size */
\r
2159 for (i=0; i<=2; i++) {
\r
2160 for (piece = WhitePawn;
\r
2161 (int) piece < (int) BlackPawn;
\r
2162 piece = (ChessSquare) ((int) piece + 1)) {
\r
2163 if (pieceBitmap[i][piece] != NULL)
\r
2164 DeleteObject(pieceBitmap[i][piece]);
\r
2168 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2169 // Orthodox Chess pieces
\r
2170 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2171 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2172 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2173 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2174 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2175 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2176 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2177 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2178 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2179 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2180 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2181 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2182 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2183 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2184 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2185 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2186 // in Shogi, Hijack the unused Queen for Lance
\r
2187 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2188 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2189 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2191 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2192 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2193 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2196 if(squareSize <= 72 && squareSize >= 33) {
\r
2197 /* A & C are available in most sizes now */
\r
2198 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2199 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2200 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2201 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2202 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2203 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2204 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2205 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2206 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2207 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2208 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2209 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2210 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2211 } else { // Smirf-like
\r
2212 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2216 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2217 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2218 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2219 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2220 } else { // WinBoard standard
\r
2221 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2222 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2223 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2228 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2229 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2232 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2233 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2234 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2235 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2236 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2237 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2238 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2239 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2240 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2241 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2242 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2243 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2244 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2245 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2246 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2247 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2248 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2249 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2250 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2260 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2261 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2262 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2263 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2264 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2265 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2266 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2267 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2268 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2269 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2270 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2271 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2272 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2280 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2281 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2282 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2283 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2284 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2285 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2288 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2289 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2290 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2291 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2292 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2293 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2294 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2295 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2296 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2297 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2298 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2299 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2300 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2301 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2302 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2306 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2307 /* special Shogi support in this size */
\r
2308 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2309 for (piece = WhitePawn;
\r
2310 (int) piece < (int) BlackPawn;
\r
2311 piece = (ChessSquare) ((int) piece + 1)) {
\r
2312 if (pieceBitmap[i][piece] != NULL)
\r
2313 DeleteObject(pieceBitmap[i][piece]);
\r
2316 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2317 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2318 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2319 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2320 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2321 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2322 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2323 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2324 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2325 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2326 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2327 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2328 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2330 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2331 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2332 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2333 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2334 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2335 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2336 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2337 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2338 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2339 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2340 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2341 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2342 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2344 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2345 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2346 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2347 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2348 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2349 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2350 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2351 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2352 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2353 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2354 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2355 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2356 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2363 PieceBitmap(ChessSquare p, int kind)
\r
2365 if ((int) p >= (int) BlackPawn)
\r
2366 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2368 return pieceBitmap[kind][(int) p];
\r
2371 /***************************************************************/
\r
2373 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2374 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2376 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2377 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2381 SquareToPos(int row, int column, int * x, int * y)
\r
2384 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2385 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2387 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2388 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2393 DrawCoordsOnDC(HDC hdc)
\r
2395 static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};
\r
2396 static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};
\r
2397 char str[2] = { NULLCHAR, NULLCHAR };
\r
2398 int oldMode, oldAlign, x, y, start, i;
\r
2402 if (!appData.showCoords)
\r
2405 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2407 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2408 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2409 oldAlign = GetTextAlign(hdc);
\r
2410 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2412 y = boardRect.top + lineGap;
\r
2413 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2415 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2416 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2417 str[0] = files[start + i];
\r
2418 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2419 y += squareSize + lineGap;
\r
2422 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2424 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2425 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2426 str[0] = ranks[start + i];
\r
2427 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2428 x += squareSize + lineGap;
\r
2431 SelectObject(hdc, oldBrush);
\r
2432 SetBkMode(hdc, oldMode);
\r
2433 SetTextAlign(hdc, oldAlign);
\r
2434 SelectObject(hdc, oldFont);
\r
2438 DrawGridOnDC(HDC hdc)
\r
2442 if (lineGap != 0) {
\r
2443 oldPen = SelectObject(hdc, gridPen);
\r
2444 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2445 SelectObject(hdc, oldPen);
\r
2449 #define HIGHLIGHT_PEN 0
\r
2450 #define PREMOVE_PEN 1
\r
2453 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2456 HPEN oldPen, hPen;
\r
2457 if (lineGap == 0) return;
\r
2459 x1 = boardRect.left +
\r
2460 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2461 y1 = boardRect.top +
\r
2462 lineGap/2 + y * (squareSize + lineGap);
\r
2464 x1 = boardRect.left +
\r
2465 lineGap/2 + x * (squareSize + lineGap);
\r
2466 y1 = boardRect.top +
\r
2467 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2469 hPen = pen ? premovePen : highlightPen;
\r
2470 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2471 MoveToEx(hdc, x1, y1, NULL);
\r
2472 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2473 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2474 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2475 LineTo(hdc, x1, y1);
\r
2476 SelectObject(hdc, oldPen);
\r
2480 DrawHighlightsOnDC(HDC hdc)
\r
2483 for (i=0; i<2; i++) {
\r
2484 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2485 DrawHighlightOnDC(hdc, TRUE,
\r
2486 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2489 for (i=0; i<2; i++) {
\r
2490 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2491 premoveHighlightInfo.sq[i].y >= 0) {
\r
2492 DrawHighlightOnDC(hdc, TRUE,
\r
2493 premoveHighlightInfo.sq[i].x,
\r
2494 premoveHighlightInfo.sq[i].y,
\r
2500 /* Note: sqcolor is used only in monoMode */
\r
2501 /* Note that this code is largely duplicated in woptions.c,
\r
2502 function DrawSampleSquare, so that needs to be updated too */
\r
2504 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2506 HBITMAP oldBitmap;
\r
2510 if (appData.blindfold) return;
\r
2512 /* [AS] Use font-based pieces if needed */
\r
2513 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2514 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2515 CreatePiecesFromFont();
\r
2517 if( fontBitmapSquareSize == squareSize ) {
\r
2518 int index = TranslatePieceToFontPiece(piece);
\r
2520 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2524 squareSize, squareSize,
\r
2529 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2533 squareSize, squareSize,
\r
2542 if (appData.monoMode) {
\r
2543 SelectObject(tmphdc, PieceBitmap(piece,
\r
2544 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2545 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2546 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2548 tmpSize = squareSize;
\r
2550 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2551 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2552 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2553 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2554 x += (squareSize - minorSize)>>1;
\r
2555 y += squareSize - minorSize - 2;
\r
2556 tmpSize = minorSize;
\r
2558 if (color || appData.allWhite ) {
\r
2559 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2561 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2562 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2563 if(appData.upsideDown && color==flipView)
\r
2564 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2566 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2567 /* Use black for outline of white pieces */
\r
2568 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2569 if(appData.upsideDown && color==flipView)
\r
2570 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2572 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2574 /* Use square color for details of black pieces */
\r
2575 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2576 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2577 if(appData.upsideDown && !flipView)
\r
2578 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2580 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2582 SelectObject(hdc, oldBrush);
\r
2583 SelectObject(tmphdc, oldBitmap);
\r
2587 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2588 int GetBackTextureMode( int algo )
\r
2590 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2594 case BACK_TEXTURE_MODE_PLAIN:
\r
2595 result = 1; /* Always use identity map */
\r
2597 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2598 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2606 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2607 to handle redraws cleanly (as random numbers would always be different).
\r
2609 VOID RebuildTextureSquareInfo()
\r
2619 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2621 if( liteBackTexture != NULL ) {
\r
2622 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2623 lite_w = bi.bmWidth;
\r
2624 lite_h = bi.bmHeight;
\r
2628 if( darkBackTexture != NULL ) {
\r
2629 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2630 dark_w = bi.bmWidth;
\r
2631 dark_h = bi.bmHeight;
\r
2635 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2636 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2637 if( (col + row) & 1 ) {
\r
2639 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2640 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2641 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2642 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2647 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2648 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2649 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2650 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2657 /* [AS] Arrow highlighting support */
\r
2659 static int A_WIDTH = 5; /* Width of arrow body */
\r
2661 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2662 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2664 static double Sqr( double x )
\r
2669 static int Round( double x )
\r
2671 return (int) (x + 0.5);
\r
2674 /* Draw an arrow between two points using current settings */
\r
2675 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2678 double dx, dy, j, k, x, y;
\r
2680 if( d_x == s_x ) {
\r
2681 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2683 arrow[0].x = s_x + A_WIDTH;
\r
2686 arrow[1].x = s_x + A_WIDTH;
\r
2687 arrow[1].y = d_y - h;
\r
2689 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2690 arrow[2].y = d_y - h;
\r
2695 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2696 arrow[4].y = d_y - h;
\r
2698 arrow[5].x = s_x - A_WIDTH;
\r
2699 arrow[5].y = d_y - h;
\r
2701 arrow[6].x = s_x - A_WIDTH;
\r
2704 else if( d_y == s_y ) {
\r
2705 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2708 arrow[0].y = s_y + A_WIDTH;
\r
2710 arrow[1].x = d_x - w;
\r
2711 arrow[1].y = s_y + A_WIDTH;
\r
2713 arrow[2].x = d_x - w;
\r
2714 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2719 arrow[4].x = d_x - w;
\r
2720 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2722 arrow[5].x = d_x - w;
\r
2723 arrow[5].y = s_y - A_WIDTH;
\r
2726 arrow[6].y = s_y - A_WIDTH;
\r
2729 /* [AS] Needed a lot of paper for this! :-) */
\r
2730 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2731 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2733 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2735 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2740 arrow[0].x = Round(x - j);
\r
2741 arrow[0].y = Round(y + j*dx);
\r
2743 arrow[1].x = Round(x + j);
\r
2744 arrow[1].y = Round(y - j*dx);
\r
2747 x = (double) d_x - k;
\r
2748 y = (double) d_y - k*dy;
\r
2751 x = (double) d_x + k;
\r
2752 y = (double) d_y + k*dy;
\r
2755 arrow[2].x = Round(x + j);
\r
2756 arrow[2].y = Round(y - j*dx);
\r
2758 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2759 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2764 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2765 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2767 arrow[6].x = Round(x - j);
\r
2768 arrow[6].y = Round(y + j*dx);
\r
2771 Polygon( hdc, arrow, 7 );
\r
2774 /* [AS] Draw an arrow between two squares */
\r
2775 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2777 int s_x, s_y, d_x, d_y;
\r
2784 if( s_col == d_col && s_row == d_row ) {
\r
2788 /* Get source and destination points */
\r
2789 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2790 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2793 d_y += squareSize / 4;
\r
2795 else if( d_y < s_y ) {
\r
2796 d_y += 3 * squareSize / 4;
\r
2799 d_y += squareSize / 2;
\r
2803 d_x += squareSize / 4;
\r
2805 else if( d_x < s_x ) {
\r
2806 d_x += 3 * squareSize / 4;
\r
2809 d_x += squareSize / 2;
\r
2812 s_x += squareSize / 2;
\r
2813 s_y += squareSize / 2;
\r
2815 /* Adjust width */
\r
2816 A_WIDTH = squareSize / 14;
\r
2819 stLB.lbStyle = BS_SOLID;
\r
2820 stLB.lbColor = appData.highlightArrowColor;
\r
2823 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2824 holdpen = SelectObject( hdc, hpen );
\r
2825 hbrush = CreateBrushIndirect( &stLB );
\r
2826 holdbrush = SelectObject( hdc, hbrush );
\r
2828 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2830 SelectObject( hdc, holdpen );
\r
2831 SelectObject( hdc, holdbrush );
\r
2832 DeleteObject( hpen );
\r
2833 DeleteObject( hbrush );
\r
2836 BOOL HasHighlightInfo()
\r
2838 BOOL result = FALSE;
\r
2840 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2841 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2849 BOOL IsDrawArrowEnabled()
\r
2851 BOOL result = FALSE;
\r
2853 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2860 VOID DrawArrowHighlight( HDC hdc )
\r
2862 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2863 DrawArrowBetweenSquares( hdc,
\r
2864 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2865 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2869 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2871 HRGN result = NULL;
\r
2873 if( HasHighlightInfo() ) {
\r
2874 int x1, y1, x2, y2;
\r
2875 int sx, sy, dx, dy;
\r
2877 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2878 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2880 sx = MIN( x1, x2 );
\r
2881 sy = MIN( y1, y2 );
\r
2882 dx = MAX( x1, x2 ) + squareSize;
\r
2883 dy = MAX( y1, y2 ) + squareSize;
\r
2885 result = CreateRectRgn( sx, sy, dx, dy );
\r
2892 Warning: this function modifies the behavior of several other functions.
\r
2894 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2895 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2896 repaint is scattered all over the place, which is not good for features such as
\r
2897 "arrow highlighting" that require a full repaint of the board.
\r
2899 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2900 user interaction, when speed is not so important) but especially to avoid errors
\r
2901 in the displayed graphics.
\r
2903 In such patched places, I always try refer to this function so there is a single
\r
2904 place to maintain knowledge.
\r
2906 To restore the original behavior, just return FALSE unconditionally.
\r
2908 BOOL IsFullRepaintPreferrable()
\r
2910 BOOL result = FALSE;
\r
2912 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2913 /* Arrow may appear on the board */
\r
2921 This function is called by DrawPosition to know whether a full repaint must
\r
2924 Only DrawPosition may directly call this function, which makes use of
\r
2925 some state information. Other function should call DrawPosition specifying
\r
2926 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2928 BOOL DrawPositionNeedsFullRepaint()
\r
2930 BOOL result = FALSE;
\r
2933 Probably a slightly better policy would be to trigger a full repaint
\r
2934 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2935 but animation is fast enough that it's difficult to notice.
\r
2937 if( animInfo.piece == EmptySquare ) {
\r
2938 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2947 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2949 int row, column, x, y, square_color, piece_color;
\r
2950 ChessSquare piece;
\r
2952 HDC texture_hdc = NULL;
\r
2954 /* [AS] Initialize background textures if needed */
\r
2955 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2956 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2957 if( backTextureSquareSize != squareSize
\r
2958 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2959 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2960 backTextureSquareSize = squareSize;
\r
2961 RebuildTextureSquareInfo();
\r
2964 texture_hdc = CreateCompatibleDC( hdc );
\r
2967 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2968 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2970 SquareToPos(row, column, &x, &y);
\r
2972 piece = board[row][column];
\r
2974 square_color = ((column + row) % 2) == 1;
\r
2975 if( gameInfo.variant == VariantXiangqi ) {
\r
2976 square_color = !InPalace(row, column);
\r
2977 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2978 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2980 piece_color = (int) piece < (int) BlackPawn;
\r
2983 /* [HGM] holdings file: light square or black */
\r
2984 if(column == BOARD_LEFT-2) {
\r
2985 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2988 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
2992 if(column == BOARD_RGHT + 1 ) {
\r
2993 if( row < gameInfo.holdingsSize )
\r
2996 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3000 if(column == BOARD_LEFT-1 ) /* left align */
\r
3001 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3002 else if( column == BOARD_RGHT) /* right align */
\r
3003 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3005 if (appData.monoMode) {
\r
3006 if (piece == EmptySquare) {
\r
3007 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3008 square_color ? WHITENESS : BLACKNESS);
\r
3010 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3013 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3014 /* [AS] Draw the square using a texture bitmap */
\r
3015 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3016 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3017 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3020 squareSize, squareSize,
\r
3023 backTextureSquareInfo[r][c].mode,
\r
3024 backTextureSquareInfo[r][c].x,
\r
3025 backTextureSquareInfo[r][c].y );
\r
3027 SelectObject( texture_hdc, hbm );
\r
3029 if (piece != EmptySquare) {
\r
3030 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3034 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3036 oldBrush = SelectObject(hdc, brush );
\r
3037 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3038 SelectObject(hdc, oldBrush);
\r
3039 if (piece != EmptySquare)
\r
3040 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3045 if( texture_hdc != NULL ) {
\r
3046 DeleteDC( texture_hdc );
\r
3050 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3051 void fputDW(FILE *f, int x)
\r
3053 fputc(x & 255, f);
\r
3054 fputc(x>>8 & 255, f);
\r
3055 fputc(x>>16 & 255, f);
\r
3056 fputc(x>>24 & 255, f);
\r
3059 #define MAX_CLIPS 200 /* more than enough */
\r
3062 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3064 // HBITMAP bufferBitmap;
\r
3069 int w = 100, h = 50;
\r
3071 if(logo == NULL) return;
\r
3072 // GetClientRect(hwndMain, &Rect);
\r
3073 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3074 // Rect.bottom-Rect.top+1);
\r
3075 tmphdc = CreateCompatibleDC(hdc);
\r
3076 hbm = SelectObject(tmphdc, logo);
\r
3077 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3081 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3082 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3083 SelectObject(tmphdc, hbm);
\r
3087 static HDC hdcSeek;
\r
3089 // [HGM] seekgraph
\r
3090 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3093 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3094 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3095 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3096 SelectObject( hdcSeek, hp );
\r
3099 // front-end wrapper for drawing functions to do rectangles
\r
3100 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3105 if (hdcSeek == NULL) {
\r
3106 hdcSeek = GetDC(hwndMain);
\r
3107 if (!appData.monoMode) {
\r
3108 SelectPalette(hdcSeek, hPal, FALSE);
\r
3109 RealizePalette(hdcSeek);
\r
3112 hp = SelectObject( hdcSeek, gridPen );
\r
3113 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3114 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3115 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3116 SelectObject( hdcSeek, hp );
\r
3119 // front-end wrapper for putting text in graph
\r
3120 void DrawSeekText(char *buf, int x, int y)
\r
3123 SetBkMode( hdcSeek, TRANSPARENT );
\r
3124 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3125 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3128 void DrawSeekDot(int x, int y, int color)
\r
3130 int square = color & 0x80;
\r
3132 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3133 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3135 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3136 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3138 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3139 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3140 SelectObject(hdcSeek, oldBrush);
\r
3144 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3146 static Board lastReq, lastDrawn;
\r
3147 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3148 static int lastDrawnFlipView = 0;
\r
3149 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3150 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3153 HBITMAP bufferBitmap;
\r
3154 HBITMAP oldBitmap;
\r
3156 HRGN clips[MAX_CLIPS];
\r
3157 ChessSquare dragged_piece = EmptySquare;
\r
3159 /* I'm undecided on this - this function figures out whether a full
\r
3160 * repaint is necessary on its own, so there's no real reason to have the
\r
3161 * caller tell it that. I think this can safely be set to FALSE - but
\r
3162 * if we trust the callers not to request full repaints unnessesarily, then
\r
3163 * we could skip some clipping work. In other words, only request a full
\r
3164 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3165 * gamestart and similar) --Hawk
\r
3167 Boolean fullrepaint = repaint;
\r
3169 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3171 if( DrawPositionNeedsFullRepaint() ) {
\r
3172 fullrepaint = TRUE;
\r
3175 if (board == NULL) {
\r
3176 if (!lastReqValid) {
\r
3181 CopyBoard(lastReq, board);
\r
3185 if (doingSizing) {
\r
3189 if (IsIconic(hwndMain)) {
\r
3193 if (hdc == NULL) {
\r
3194 hdc = GetDC(hwndMain);
\r
3195 if (!appData.monoMode) {
\r
3196 SelectPalette(hdc, hPal, FALSE);
\r
3197 RealizePalette(hdc);
\r
3201 releaseDC = FALSE;
\r
3204 /* Create some work-DCs */
\r
3205 hdcmem = CreateCompatibleDC(hdc);
\r
3206 tmphdc = CreateCompatibleDC(hdc);
\r
3208 /* If dragging is in progress, we temporarely remove the piece */
\r
3209 /* [HGM] or temporarily decrease count if stacked */
\r
3210 /* !! Moved to before board compare !! */
\r
3211 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3212 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3213 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3214 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3215 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3217 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3218 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3219 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3221 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3224 /* Figure out which squares need updating by comparing the
\r
3225 * newest board with the last drawn board and checking if
\r
3226 * flipping has changed.
\r
3228 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3229 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3230 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3231 if (lastDrawn[row][column] != board[row][column]) {
\r
3232 SquareToPos(row, column, &x, &y);
\r
3233 clips[num_clips++] =
\r
3234 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3238 for (i=0; i<2; i++) {
\r
3239 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3240 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3241 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3242 lastDrawnHighlight.sq[i].y >= 0) {
\r
3243 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3244 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3245 clips[num_clips++] =
\r
3246 CreateRectRgn(x - lineGap, y - lineGap,
\r
3247 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3249 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3250 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3251 clips[num_clips++] =
\r
3252 CreateRectRgn(x - lineGap, y - lineGap,
\r
3253 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3257 for (i=0; i<2; i++) {
\r
3258 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3259 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3260 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3261 lastDrawnPremove.sq[i].y >= 0) {
\r
3262 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3263 lastDrawnPremove.sq[i].x, &x, &y);
\r
3264 clips[num_clips++] =
\r
3265 CreateRectRgn(x - lineGap, y - lineGap,
\r
3266 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3268 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3269 premoveHighlightInfo.sq[i].y >= 0) {
\r
3270 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3271 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3272 clips[num_clips++] =
\r
3273 CreateRectRgn(x - lineGap, y - lineGap,
\r
3274 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3279 fullrepaint = TRUE;
\r
3282 /* Create a buffer bitmap - this is the actual bitmap
\r
3283 * being written to. When all the work is done, we can
\r
3284 * copy it to the real DC (the screen). This avoids
\r
3285 * the problems with flickering.
\r
3287 GetClientRect(hwndMain, &Rect);
\r
3288 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3289 Rect.bottom-Rect.top+1);
\r
3290 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3291 if (!appData.monoMode) {
\r
3292 SelectPalette(hdcmem, hPal, FALSE);
\r
3295 /* Create clips for dragging */
\r
3296 if (!fullrepaint) {
\r
3297 if (dragInfo.from.x >= 0) {
\r
3298 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3299 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3301 if (dragInfo.start.x >= 0) {
\r
3302 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3303 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3305 if (dragInfo.pos.x >= 0) {
\r
3306 x = dragInfo.pos.x - squareSize / 2;
\r
3307 y = dragInfo.pos.y - squareSize / 2;
\r
3308 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3310 if (dragInfo.lastpos.x >= 0) {
\r
3311 x = dragInfo.lastpos.x - squareSize / 2;
\r
3312 y = dragInfo.lastpos.y - squareSize / 2;
\r
3313 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3317 /* Are we animating a move?
\r
3319 * - remove the piece from the board (temporarely)
\r
3320 * - calculate the clipping region
\r
3322 if (!fullrepaint) {
\r
3323 if (animInfo.piece != EmptySquare) {
\r
3324 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3325 x = boardRect.left + animInfo.lastpos.x;
\r
3326 y = boardRect.top + animInfo.lastpos.y;
\r
3327 x2 = boardRect.left + animInfo.pos.x;
\r
3328 y2 = boardRect.top + animInfo.pos.y;
\r
3329 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3330 /* Slight kludge. The real problem is that after AnimateMove is
\r
3331 done, the position on the screen does not match lastDrawn.
\r
3332 This currently causes trouble only on e.p. captures in
\r
3333 atomic, where the piece moves to an empty square and then
\r
3334 explodes. The old and new positions both had an empty square
\r
3335 at the destination, but animation has drawn a piece there and
\r
3336 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3337 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3341 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3342 if (num_clips == 0)
\r
3343 fullrepaint = TRUE;
\r
3345 /* Set clipping on the memory DC */
\r
3346 if (!fullrepaint) {
\r
3347 SelectClipRgn(hdcmem, clips[0]);
\r
3348 for (x = 1; x < num_clips; x++) {
\r
3349 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3350 abort(); // this should never ever happen!
\r
3354 /* Do all the drawing to the memory DC */
\r
3355 if(explodeInfo.radius) { // [HGM] atomic
\r
3357 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3358 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3359 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3360 x += squareSize/2;
\r
3361 y += squareSize/2;
\r
3362 if(!fullrepaint) {
\r
3363 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3364 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3366 DrawGridOnDC(hdcmem);
\r
3367 DrawHighlightsOnDC(hdcmem);
\r
3368 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3369 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3370 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3371 SelectObject(hdcmem, oldBrush);
\r
3373 DrawGridOnDC(hdcmem);
\r
3374 DrawHighlightsOnDC(hdcmem);
\r
3375 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3377 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3378 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3379 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3380 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3381 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3382 SquareToPos(row, column, &x, &y);
\r
3383 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3384 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3385 SelectObject(hdcmem, oldBrush);
\r
3390 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3391 if(appData.autoLogo) {
\r
3393 switch(gameMode) { // pick logos based on game mode
\r
3394 case IcsObserving:
\r
3395 whiteLogo = second.programLogo; // ICS logo
\r
3396 blackLogo = second.programLogo;
\r
3399 case IcsPlayingWhite:
\r
3400 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3401 blackLogo = second.programLogo; // ICS logo
\r
3403 case IcsPlayingBlack:
\r
3404 whiteLogo = second.programLogo; // ICS logo
\r
3405 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3407 case TwoMachinesPlay:
\r
3408 if(first.twoMachinesColor[0] == 'b') {
\r
3409 whiteLogo = second.programLogo;
\r
3410 blackLogo = first.programLogo;
\r
3413 case MachinePlaysWhite:
\r
3414 blackLogo = userLogo;
\r
3416 case MachinePlaysBlack:
\r
3417 whiteLogo = userLogo;
\r
3418 blackLogo = first.programLogo;
\r
3421 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3422 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3425 if( appData.highlightMoveWithArrow ) {
\r
3426 DrawArrowHighlight(hdcmem);
\r
3429 DrawCoordsOnDC(hdcmem);
\r
3431 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3432 /* to make sure lastDrawn contains what is actually drawn */
\r
3434 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3435 if (dragged_piece != EmptySquare) {
\r
3436 /* [HGM] or restack */
\r
3437 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3438 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3440 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3441 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3442 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3443 x = dragInfo.pos.x - squareSize / 2;
\r
3444 y = dragInfo.pos.y - squareSize / 2;
\r
3445 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3446 ((int) dragged_piece < (int) BlackPawn),
\r
3447 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3450 /* Put the animated piece back into place and draw it */
\r
3451 if (animInfo.piece != EmptySquare) {
\r
3452 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3453 x = boardRect.left + animInfo.pos.x;
\r
3454 y = boardRect.top + animInfo.pos.y;
\r
3455 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3456 ((int) animInfo.piece < (int) BlackPawn),
\r
3457 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3460 /* Release the bufferBitmap by selecting in the old bitmap
\r
3461 * and delete the memory DC
\r
3463 SelectObject(hdcmem, oldBitmap);
\r
3466 /* Set clipping on the target DC */
\r
3467 if (!fullrepaint) {
\r
3468 SelectClipRgn(hdc, clips[0]);
\r
3469 for (x = 1; x < num_clips; x++) {
\r
3470 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3471 abort(); // this should never ever happen!
\r
3475 /* Copy the new bitmap onto the screen in one go.
\r
3476 * This way we avoid any flickering
\r
3478 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3479 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3480 boardRect.right - boardRect.left,
\r
3481 boardRect.bottom - boardRect.top,
\r
3482 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3483 if(saveDiagFlag) {
\r
3484 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3485 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3487 GetObject(bufferBitmap, sizeof(b), &b);
\r
3488 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3489 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3490 bih.biWidth = b.bmWidth;
\r
3491 bih.biHeight = b.bmHeight;
\r
3493 bih.biBitCount = b.bmBitsPixel;
\r
3494 bih.biCompression = 0;
\r
3495 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3496 bih.biXPelsPerMeter = 0;
\r
3497 bih.biYPelsPerMeter = 0;
\r
3498 bih.biClrUsed = 0;
\r
3499 bih.biClrImportant = 0;
\r
3500 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3501 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3502 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3503 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3505 wb = b.bmWidthBytes;
\r
3507 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3508 int k = ((int*) pData)[i];
\r
3509 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3510 if(j >= 16) break;
\r
3512 if(j >= nrColors) nrColors = j+1;
\r
3514 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3516 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3517 for(w=0; w<(wb>>2); w+=2) {
\r
3518 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3519 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3520 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3521 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3522 pData[p++] = m | j<<4;
\r
3524 while(p&3) pData[p++] = 0;
\r
3527 wb = ((wb+31)>>5)<<2;
\r
3529 // write BITMAPFILEHEADER
\r
3530 fprintf(diagFile, "BM");
\r
3531 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3532 fputDW(diagFile, 0);
\r
3533 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3534 // write BITMAPINFOHEADER
\r
3535 fputDW(diagFile, 40);
\r
3536 fputDW(diagFile, b.bmWidth);
\r
3537 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3538 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3539 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3540 fputDW(diagFile, 0);
\r
3541 fputDW(diagFile, 0);
\r
3542 fputDW(diagFile, 0);
\r
3543 fputDW(diagFile, 0);
\r
3544 fputDW(diagFile, 0);
\r
3545 fputDW(diagFile, 0);
\r
3546 // write color table
\r
3548 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3549 // write bitmap data
\r
3550 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3551 fputc(pData[i], diagFile);
\r
3555 SelectObject(tmphdc, oldBitmap);
\r
3557 /* Massive cleanup */
\r
3558 for (x = 0; x < num_clips; x++)
\r
3559 DeleteObject(clips[x]);
\r
3562 DeleteObject(bufferBitmap);
\r
3565 ReleaseDC(hwndMain, hdc);
\r
3567 if (lastDrawnFlipView != flipView) {
\r
3569 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3571 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3574 /* CopyBoard(lastDrawn, board);*/
\r
3575 lastDrawnHighlight = highlightInfo;
\r
3576 lastDrawnPremove = premoveHighlightInfo;
\r
3577 lastDrawnFlipView = flipView;
\r
3578 lastDrawnValid = 1;
\r
3581 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3586 saveDiagFlag = 1; diagFile = f;
\r
3587 HDCDrawPosition(NULL, TRUE, NULL);
\r
3591 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3598 /*---------------------------------------------------------------------------*\
\r
3599 | CLIENT PAINT PROCEDURE
\r
3600 | This is the main event-handler for the WM_PAINT message.
\r
3602 \*---------------------------------------------------------------------------*/
\r
3604 PaintProc(HWND hwnd)
\r
3610 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3611 if (IsIconic(hwnd)) {
\r
3612 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3614 if (!appData.monoMode) {
\r
3615 SelectPalette(hdc, hPal, FALSE);
\r
3616 RealizePalette(hdc);
\r
3618 HDCDrawPosition(hdc, 1, NULL);
\r
3620 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3621 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3622 ETO_CLIPPED|ETO_OPAQUE,
\r
3623 &messageRect, messageText, strlen(messageText), NULL);
\r
3624 SelectObject(hdc, oldFont);
\r
3625 DisplayBothClocks();
\r
3627 EndPaint(hwnd,&ps);
\r
3635 * If the user selects on a border boundary, return -1; if off the board,
\r
3636 * return -2. Otherwise map the event coordinate to the square.
\r
3637 * The offset boardRect.left or boardRect.top must already have been
\r
3638 * subtracted from x.
\r
3640 int EventToSquare(x, limit)
\r
3648 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3650 x /= (squareSize + lineGap);
\r
3662 DropEnable dropEnables[] = {
\r
3663 { 'P', DP_Pawn, "Pawn" },
\r
3664 { 'N', DP_Knight, "Knight" },
\r
3665 { 'B', DP_Bishop, "Bishop" },
\r
3666 { 'R', DP_Rook, "Rook" },
\r
3667 { 'Q', DP_Queen, "Queen" },
\r
3671 SetupDropMenu(HMENU hmenu)
\r
3673 int i, count, enable;
\r
3675 extern char white_holding[], black_holding[];
\r
3676 char item[MSG_SIZ];
\r
3678 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3679 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3680 dropEnables[i].piece);
\r
3682 while (p && *p++ == dropEnables[i].piece) count++;
\r
3683 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3684 enable = count > 0 || !appData.testLegality
\r
3685 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3686 && !appData.icsActive);
\r
3687 ModifyMenu(hmenu, dropEnables[i].command,
\r
3688 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3689 dropEnables[i].command, item);
\r
3693 void DragPieceBegin(int x, int y)
\r
3695 dragInfo.lastpos.x = boardRect.left + x;
\r
3696 dragInfo.lastpos.y = boardRect.top + y;
\r
3697 dragInfo.from.x = fromX;
\r
3698 dragInfo.from.y = fromY;
\r
3699 dragInfo.start = dragInfo.from;
\r
3700 SetCapture(hwndMain);
\r
3703 void DragPieceEnd(int x, int y)
\r
3706 dragInfo.start.x = dragInfo.start.y = -1;
\r
3707 dragInfo.from = dragInfo.start;
\r
3708 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3711 /* Event handler for mouse messages */
\r
3713 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3717 static int recursive = 0;
\r
3719 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3722 if (message == WM_MBUTTONUP) {
\r
3723 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3724 to the middle button: we simulate pressing the left button too!
\r
3726 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3727 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3733 pt.x = LOWORD(lParam);
\r
3734 pt.y = HIWORD(lParam);
\r
3735 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3736 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3737 if (!flipView && y >= 0) {
\r
3738 y = BOARD_HEIGHT - 1 - y;
\r
3740 if (flipView && x >= 0) {
\r
3741 x = BOARD_WIDTH - 1 - x;
\r
3744 switch (message) {
\r
3745 case WM_LBUTTONDOWN:
\r
3746 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3747 if (gameMode == EditPosition) {
\r
3748 SetWhiteToPlayEvent();
\r
3749 } else if (gameMode == IcsPlayingBlack ||
\r
3750 gameMode == MachinePlaysWhite) {
\r
3752 } else if (gameMode == EditGame) {
\r
3753 AdjustClock(flipClock, -1);
\r
3755 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3756 if (gameMode == EditPosition) {
\r
3757 SetBlackToPlayEvent();
\r
3758 } else if (gameMode == IcsPlayingWhite ||
\r
3759 gameMode == MachinePlaysBlack) {
\r
3761 } else if (gameMode == EditGame) {
\r
3762 AdjustClock(!flipClock, -1);
\r
3765 dragInfo.start.x = dragInfo.start.y = -1;
\r
3766 dragInfo.from = dragInfo.start;
\r
3767 if(fromX == -1 && frozen) { // not sure where this is for
\r
3768 fromX = fromY = -1;
\r
3769 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3772 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3773 DrawPosition(TRUE, NULL);
\r
3776 case WM_LBUTTONUP:
\r
3777 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3778 DrawPosition(TRUE, NULL);
\r
3781 case WM_MOUSEMOVE:
\r
3782 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3783 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3784 if ((appData.animateDragging || appData.highlightDragging)
\r
3785 && (wParam & MK_LBUTTON)
\r
3786 && dragInfo.from.x >= 0)
\r
3788 BOOL full_repaint = FALSE;
\r
3790 if (appData.animateDragging) {
\r
3791 dragInfo.pos = pt;
\r
3793 if (appData.highlightDragging) {
\r
3794 SetHighlights(fromX, fromY, x, y);
\r
3795 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3796 full_repaint = TRUE;
\r
3800 DrawPosition( full_repaint, NULL);
\r
3802 dragInfo.lastpos = dragInfo.pos;
\r
3806 case WM_MOUSEWHEEL: // [DM]
\r
3807 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3808 /* Mouse Wheel is being rolled forward
\r
3809 * Play moves forward
\r
3811 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3812 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3813 /* Mouse Wheel is being rolled backward
\r
3814 * Play moves backward
\r
3816 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3817 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3821 case WM_MBUTTONUP:
\r
3822 case WM_RBUTTONUP:
\r
3824 { extern int promotionChoice; extern Board promoBoard;
\r
3825 if(promotionChoice == 3 && (promoBoard[y][x] == EmptySquare || x == fromX && y == fromY))
\r
3826 break; // [HGM] promopopup: suppression of up-click selection as long as pseudo-popup is used
\r
3828 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3831 case WM_MBUTTONDOWN:
\r
3832 case WM_RBUTTONDOWN:
\r
3835 fromX = fromY = -1;
\r
3836 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3837 dragInfo.start.x = dragInfo.start.y = -1;
\r
3838 dragInfo.from = dragInfo.start;
\r
3839 dragInfo.lastpos = dragInfo.pos;
\r
3840 if (appData.highlightDragging) {
\r
3841 ClearHighlights();
\r
3844 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3845 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3846 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3847 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3848 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3851 DrawPosition(TRUE, NULL);
\r
3853 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3856 if (message == WM_MBUTTONDOWN) {
\r
3857 buttonCount = 3; /* even if system didn't think so */
\r
3858 if (wParam & MK_SHIFT)
\r
3859 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3861 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3862 } else { /* message == WM_RBUTTONDOWN */
\r
3863 /* Just have one menu, on the right button. Windows users don't
\r
3864 think to try the middle one, and sometimes other software steals
\r
3865 it, or it doesn't really exist. */
\r
3866 if(gameInfo.variant != VariantShogi)
\r
3867 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3869 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3873 SetCapture(hwndMain);
3876 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3877 SetupDropMenu(hmenu);
\r
3878 MenuPopup(hwnd, pt, hmenu, -1);
\r
3888 /* Preprocess messages for buttons in main window */
\r
3890 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3892 int id = GetWindowLong(hwnd, GWL_ID);
\r
3895 for (i=0; i<N_BUTTONS; i++) {
\r
3896 if (buttonDesc[i].id == id) break;
\r
3898 if (i == N_BUTTONS) return 0;
\r
3899 switch (message) {
\r
3904 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3905 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3912 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3915 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3916 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3917 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3918 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3920 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3922 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3923 PopUpMoveDialog((char)wParam);
\r
3929 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3932 /* Process messages for Promotion dialog box */
\r
3934 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3938 switch (message) {
\r
3939 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3940 /* Center the dialog over the application window */
\r
3941 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3942 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3943 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3944 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3945 SW_SHOW : SW_HIDE);
\r
3946 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3947 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3948 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3949 PieceToChar(WhiteAngel) != '~') ||
\r
3950 (PieceToChar(BlackAngel) >= 'A' &&
\r
3951 PieceToChar(BlackAngel) != '~') ) ?
\r
3952 SW_SHOW : SW_HIDE);
\r
3953 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3954 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3955 PieceToChar(WhiteMarshall) != '~') ||
\r
3956 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3957 PieceToChar(BlackMarshall) != '~') ) ?
\r
3958 SW_SHOW : SW_HIDE);
\r
3959 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3960 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3961 gameInfo.variant != VariantShogi ?
\r
3962 SW_SHOW : SW_HIDE);
\r
3963 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3964 gameInfo.variant != VariantShogi ?
\r
3965 SW_SHOW : SW_HIDE);
\r
3966 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3967 gameInfo.variant == VariantShogi ?
\r
3968 SW_SHOW : SW_HIDE);
\r
3969 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3970 gameInfo.variant == VariantShogi ?
\r
3971 SW_SHOW : SW_HIDE);
\r
3972 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3973 gameInfo.variant == VariantSuper ?
\r
3974 SW_SHOW : SW_HIDE);
\r
3977 case WM_COMMAND: /* message: received a command */
\r
3978 switch (LOWORD(wParam)) {
\r
3980 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3981 ClearHighlights();
\r
3982 DrawPosition(FALSE, NULL);
\r
3985 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3988 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3991 promoChar = PieceToChar(BlackRook);
\r
3994 promoChar = PieceToChar(BlackBishop);
\r
3996 case PB_Chancellor:
\r
3997 promoChar = PieceToChar(BlackMarshall);
\r
3999 case PB_Archbishop:
\r
4000 promoChar = PieceToChar(BlackAngel);
\r
4003 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4008 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4009 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4010 only show the popup when we are already sure the move is valid or
\r
4011 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4012 will figure out it is a promotion from the promoChar. */
\r
4013 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4014 fromX = fromY = -1;
\r
4015 if (!appData.highlightLastMove) {
\r
4016 ClearHighlights();
\r
4017 DrawPosition(FALSE, NULL);
\r
4024 /* Pop up promotion dialog */
\r
4026 PromotionPopup(HWND hwnd)
\r
4030 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4031 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4032 hwnd, (DLGPROC)lpProc);
\r
4033 FreeProcInstance(lpProc);
\r
4039 DrawPosition(TRUE, NULL);
\r
4040 PromotionPopup(hwndMain);
\r
4044 PromoDialog(int h, int w, Board board, Boolean clearBoard, char *title, int x, int y)
\r
4045 { // dummy routine to mimic with pseudo-popup what front-end should do:
\r
4046 // display a popup with h x w mini-board, and divert any mouse clicks
\r
4047 // on it to the back-end routines RightClick and LeftClick, just
\r
4048 // like the mouse event hadler of the board widget does now.
\r
4049 // (Note it would have to off-set x if holdings are displayed!)
\r
4050 DisplayMessage("Click on your piece of choice", "");
\r
4051 DrawPosition(TRUE, board);
\r
4054 /* Toggle ShowThinking */
\r
4056 ToggleShowThinking()
\r
4058 appData.showThinking = !appData.showThinking;
\r
4059 ShowThinkingEvent();
\r
4063 LoadGameDialog(HWND hwnd, char* title)
\r
4067 char fileTitle[MSG_SIZ];
\r
4068 f = OpenFileDialog(hwnd, "rb", "",
\r
4069 appData.oldSaveStyle ? "gam" : "pgn",
\r
4071 title, &number, fileTitle, NULL);
\r
4073 cmailMsgLoaded = FALSE;
\r
4074 if (number == 0) {
\r
4075 int error = GameListBuild(f);
\r
4077 DisplayError("Cannot build game list", error);
\r
4078 } else if (!ListEmpty(&gameList) &&
\r
4079 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4080 GameListPopUp(f, fileTitle);
\r
4083 GameListDestroy();
\r
4086 LoadGame(f, number, fileTitle, FALSE);
\r
4090 int get_term_width()
\r
4095 HFONT hfont, hold_font;
\r
4100 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4104 // get the text metrics
\r
4105 hdc = GetDC(hText);
\r
4106 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4107 if (consoleCF.dwEffects & CFE_BOLD)
\r
4108 lf.lfWeight = FW_BOLD;
\r
4109 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4110 lf.lfItalic = TRUE;
\r
4111 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4112 lf.lfStrikeOut = TRUE;
\r
4113 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4114 lf.lfUnderline = TRUE;
\r
4115 hfont = CreateFontIndirect(&lf);
\r
4116 hold_font = SelectObject(hdc, hfont);
\r
4117 GetTextMetrics(hdc, &tm);
\r
4118 SelectObject(hdc, hold_font);
\r
4119 DeleteObject(hfont);
\r
4120 ReleaseDC(hText, hdc);
\r
4122 // get the rectangle
\r
4123 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4125 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4128 void UpdateICSWidth(HWND hText)
\r
4130 LONG old_width, new_width;
\r
4132 new_width = get_term_width(hText, FALSE);
\r
4133 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4134 if (new_width != old_width)
\r
4136 ics_update_width(new_width);
\r
4137 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4142 ChangedConsoleFont()
\r
4145 CHARRANGE tmpsel, sel;
\r
4146 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4147 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4148 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4151 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4152 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4153 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4154 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4155 * size. This was undocumented in the version of MSVC++ that I had
\r
4156 * when I wrote the code, but is apparently documented now.
\r
4158 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4159 cfmt.bCharSet = f->lf.lfCharSet;
\r
4160 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4161 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4162 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4163 /* Why are the following seemingly needed too? */
\r
4164 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4165 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4166 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4168 tmpsel.cpMax = -1; /*999999?*/
\r
4169 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4170 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4171 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4172 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4174 paraf.cbSize = sizeof(paraf);
\r
4175 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4176 paraf.dxStartIndent = 0;
\r
4177 paraf.dxOffset = WRAP_INDENT;
\r
4178 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4179 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4180 UpdateICSWidth(hText);
\r
4183 /*---------------------------------------------------------------------------*\
\r
4185 * Window Proc for main window
\r
4187 \*---------------------------------------------------------------------------*/
\r
4189 /* Process messages for main window, etc. */
\r
4191 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4194 int wmId, wmEvent;
\r
4198 char fileTitle[MSG_SIZ];
\r
4199 char buf[MSG_SIZ];
\r
4200 static SnapData sd;
\r
4202 switch (message) {
\r
4204 case WM_PAINT: /* message: repaint portion of window */
\r
4208 case WM_ERASEBKGND:
\r
4209 if (IsIconic(hwnd)) {
\r
4210 /* Cheat; change the message */
\r
4211 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4213 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4217 case WM_LBUTTONDOWN:
\r
4218 case WM_MBUTTONDOWN:
\r
4219 case WM_RBUTTONDOWN:
\r
4220 case WM_LBUTTONUP:
\r
4221 case WM_MBUTTONUP:
\r
4222 case WM_RBUTTONUP:
\r
4223 case WM_MOUSEMOVE:
\r
4224 case WM_MOUSEWHEEL:
\r
4225 MouseEvent(hwnd, message, wParam, lParam);
\r
4228 JAWS_KB_NAVIGATION
\r
4232 JAWS_ALT_INTERCEPT
\r
4234 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4235 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4236 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4237 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4239 SendMessage(h, message, wParam, lParam);
\r
4240 } else if(lParam != KF_REPEAT) {
\r
4241 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4242 PopUpMoveDialog((char)wParam);
\r
4243 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4244 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4249 case WM_PALETTECHANGED:
\r
4250 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4252 HDC hdc = GetDC(hwndMain);
\r
4253 SelectPalette(hdc, hPal, TRUE);
\r
4254 nnew = RealizePalette(hdc);
\r
4256 paletteChanged = TRUE;
\r
4257 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4259 ReleaseDC(hwnd, hdc);
\r
4263 case WM_QUERYNEWPALETTE:
\r
4264 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4266 HDC hdc = GetDC(hwndMain);
\r
4267 paletteChanged = FALSE;
\r
4268 SelectPalette(hdc, hPal, FALSE);
\r
4269 nnew = RealizePalette(hdc);
\r
4271 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4273 ReleaseDC(hwnd, hdc);
\r
4278 case WM_COMMAND: /* message: command from application menu */
\r
4279 wmId = LOWORD(wParam);
\r
4280 wmEvent = HIWORD(wParam);
\r
4285 SAY("new game enter a move to play against the computer with white");
\r
4288 case IDM_NewGameFRC:
\r
4289 if( NewGameFRC() == 0 ) {
\r
4294 case IDM_NewVariant:
\r
4295 NewVariantPopup(hwnd);
\r
4298 case IDM_LoadGame:
\r
4299 LoadGameDialog(hwnd, "Load Game from File");
\r
4302 case IDM_LoadNextGame:
\r
4306 case IDM_LoadPrevGame:
\r
4310 case IDM_ReloadGame:
\r
4314 case IDM_LoadPosition:
\r
4315 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4316 Reset(FALSE, TRUE);
\r
4319 f = OpenFileDialog(hwnd, "rb", "",
\r
4320 appData.oldSaveStyle ? "pos" : "fen",
\r
4322 "Load Position from File", &number, fileTitle, NULL);
\r
4324 LoadPosition(f, number, fileTitle);
\r
4328 case IDM_LoadNextPosition:
\r
4329 ReloadPosition(1);
\r
4332 case IDM_LoadPrevPosition:
\r
4333 ReloadPosition(-1);
\r
4336 case IDM_ReloadPosition:
\r
4337 ReloadPosition(0);
\r
4340 case IDM_SaveGame:
\r
4341 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4342 f = OpenFileDialog(hwnd, "a", defName,
\r
4343 appData.oldSaveStyle ? "gam" : "pgn",
\r
4345 "Save Game to File", NULL, fileTitle, NULL);
\r
4347 SaveGame(f, 0, "");
\r
4351 case IDM_SavePosition:
\r
4352 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4353 f = OpenFileDialog(hwnd, "a", defName,
\r
4354 appData.oldSaveStyle ? "pos" : "fen",
\r
4356 "Save Position to File", NULL, fileTitle, NULL);
\r
4358 SavePosition(f, 0, "");
\r
4362 case IDM_SaveDiagram:
\r
4363 defName = "diagram";
\r
4364 f = OpenFileDialog(hwnd, "wb", defName,
\r
4367 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4373 case IDM_CopyGame:
\r
4374 CopyGameToClipboard();
\r
4377 case IDM_PasteGame:
\r
4378 PasteGameFromClipboard();
\r
4381 case IDM_CopyGameListToClipboard:
\r
4382 CopyGameListToClipboard();
\r
4385 /* [AS] Autodetect FEN or PGN data */
\r
4386 case IDM_PasteAny:
\r
4387 PasteGameOrFENFromClipboard();
\r
4390 /* [AS] Move history */
\r
4391 case IDM_ShowMoveHistory:
\r
4392 if( MoveHistoryIsUp() ) {
\r
4393 MoveHistoryPopDown();
\r
4396 MoveHistoryPopUp();
\r
4400 /* [AS] Eval graph */
\r
4401 case IDM_ShowEvalGraph:
\r
4402 if( EvalGraphIsUp() ) {
\r
4403 EvalGraphPopDown();
\r
4407 SetFocus(hwndMain);
\r
4411 /* [AS] Engine output */
\r
4412 case IDM_ShowEngineOutput:
\r
4413 if( EngineOutputIsUp() ) {
\r
4414 EngineOutputPopDown();
\r
4417 EngineOutputPopUp();
\r
4421 /* [AS] User adjudication */
\r
4422 case IDM_UserAdjudication_White:
\r
4423 UserAdjudicationEvent( +1 );
\r
4426 case IDM_UserAdjudication_Black:
\r
4427 UserAdjudicationEvent( -1 );
\r
4430 case IDM_UserAdjudication_Draw:
\r
4431 UserAdjudicationEvent( 0 );
\r
4434 /* [AS] Game list options dialog */
\r
4435 case IDM_GameListOptions:
\r
4436 GameListOptions();
\r
4443 case IDM_CopyPosition:
\r
4444 CopyFENToClipboard();
\r
4447 case IDM_PastePosition:
\r
4448 PasteFENFromClipboard();
\r
4451 case IDM_MailMove:
\r
4455 case IDM_ReloadCMailMsg:
\r
4456 Reset(TRUE, TRUE);
\r
4457 ReloadCmailMsgEvent(FALSE);
\r
4460 case IDM_Minimize:
\r
4461 ShowWindow(hwnd, SW_MINIMIZE);
\r
4468 case IDM_MachineWhite:
\r
4469 MachineWhiteEvent();
\r
4471 * refresh the tags dialog only if it's visible
\r
4473 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4475 tags = PGNTags(&gameInfo);
\r
4476 TagsPopUp(tags, CmailMsg());
\r
4479 SAY("computer starts playing white");
\r
4482 case IDM_MachineBlack:
\r
4483 MachineBlackEvent();
\r
4485 * refresh the tags dialog only if it's visible
\r
4487 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4489 tags = PGNTags(&gameInfo);
\r
4490 TagsPopUp(tags, CmailMsg());
\r
4493 SAY("computer starts playing black");
\r
4496 case IDM_TwoMachines:
\r
4497 TwoMachinesEvent();
\r
4499 * refresh the tags dialog only if it's visible
\r
4501 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4503 tags = PGNTags(&gameInfo);
\r
4504 TagsPopUp(tags, CmailMsg());
\r
4507 SAY("programs start playing each other");
\r
4510 case IDM_AnalysisMode:
\r
4511 if (!first.analysisSupport) {
\r
4512 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4513 DisplayError(buf, 0);
\r
4515 SAY("analyzing current position");
\r
4516 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4517 if (appData.icsActive) {
\r
4518 if (gameMode != IcsObserving) {
\r
4519 sprintf(buf, "You are not observing a game");
\r
4520 DisplayError(buf, 0);
\r
4521 /* secure check */
\r
4522 if (appData.icsEngineAnalyze) {
\r
4523 if (appData.debugMode)
\r
4524 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4525 ExitAnalyzeMode();
\r
4531 /* if enable, user want disable icsEngineAnalyze */
\r
4532 if (appData.icsEngineAnalyze) {
\r
4533 ExitAnalyzeMode();
\r
4537 appData.icsEngineAnalyze = TRUE;
\r
4538 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4541 if (!appData.showThinking) ToggleShowThinking();
\r
4542 AnalyzeModeEvent();
\r
4546 case IDM_AnalyzeFile:
\r
4547 if (!first.analysisSupport) {
\r
4548 char buf[MSG_SIZ];
\r
4549 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4550 DisplayError(buf, 0);
\r
4552 if (!appData.showThinking) ToggleShowThinking();
\r
4553 AnalyzeFileEvent();
\r
4554 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4555 AnalysisPeriodicEvent(1);
\r
4559 case IDM_IcsClient:
\r
4563 case IDM_EditGame:
\r
4568 case IDM_EditPosition:
\r
4569 EditPositionEvent();
\r
4570 SAY("to set up a position type a FEN");
\r
4573 case IDM_Training:
\r
4577 case IDM_ShowGameList:
\r
4578 ShowGameListProc();
\r
4581 case IDM_EditTags:
\r
4585 case IDM_EditComment:
\r
4586 if (commentUp && editComment) {
\r
4589 EditCommentEvent();
\r
4609 case IDM_CallFlag:
\r
4629 case IDM_StopObserving:
\r
4630 StopObservingEvent();
\r
4633 case IDM_StopExamining:
\r
4634 StopExaminingEvent();
\r
4637 case IDM_TypeInMove:
\r
4638 PopUpMoveDialog('\000');
\r
4641 case IDM_TypeInName:
\r
4642 PopUpNameDialog('\000');
\r
4645 case IDM_Backward:
\r
4647 SetFocus(hwndMain);
\r
4654 SetFocus(hwndMain);
\r
4659 SetFocus(hwndMain);
\r
4664 SetFocus(hwndMain);
\r
4671 case IDM_TruncateGame:
\r
4672 TruncateGameEvent();
\r
4679 case IDM_RetractMove:
\r
4680 RetractMoveEvent();
\r
4683 case IDM_FlipView:
\r
4684 flipView = !flipView;
\r
4685 DrawPosition(FALSE, NULL);
\r
4688 case IDM_FlipClock:
\r
4689 flipClock = !flipClock;
\r
4690 DisplayBothClocks();
\r
4691 DrawPosition(FALSE, NULL);
\r
4694 case IDM_MuteSounds:
\r
4695 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4696 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4697 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4700 case IDM_GeneralOptions:
\r
4701 GeneralOptionsPopup(hwnd);
\r
4702 DrawPosition(TRUE, NULL);
\r
4705 case IDM_BoardOptions:
\r
4706 BoardOptionsPopup(hwnd);
\r
4709 case IDM_EnginePlayOptions:
\r
4710 EnginePlayOptionsPopup(hwnd);
\r
4713 case IDM_Engine1Options:
\r
4714 EngineOptionsPopup(hwnd, &first);
\r
4717 case IDM_Engine2Options:
\r
4718 EngineOptionsPopup(hwnd, &second);
\r
4721 case IDM_OptionsUCI:
\r
4722 UciOptionsPopup(hwnd);
\r
4725 case IDM_IcsOptions:
\r
4726 IcsOptionsPopup(hwnd);
\r
4730 FontsOptionsPopup(hwnd);
\r
4734 SoundOptionsPopup(hwnd);
\r
4737 case IDM_CommPort:
\r
4738 CommPortOptionsPopup(hwnd);
\r
4741 case IDM_LoadOptions:
\r
4742 LoadOptionsPopup(hwnd);
\r
4745 case IDM_SaveOptions:
\r
4746 SaveOptionsPopup(hwnd);
\r
4749 case IDM_TimeControl:
\r
4750 TimeControlOptionsPopup(hwnd);
\r
4753 case IDM_SaveSettings:
\r
4754 SaveSettings(settingsFileName);
\r
4757 case IDM_SaveSettingsOnExit:
\r
4758 saveSettingsOnExit = !saveSettingsOnExit;
\r
4759 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4760 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4761 MF_CHECKED : MF_UNCHECKED));
\r
4772 case IDM_AboutGame:
\r
4777 appData.debugMode = !appData.debugMode;
\r
4778 if (appData.debugMode) {
\r
4779 char dir[MSG_SIZ];
\r
4780 GetCurrentDirectory(MSG_SIZ, dir);
\r
4781 SetCurrentDirectory(installDir);
\r
4782 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4783 SetCurrentDirectory(dir);
\r
4784 setbuf(debugFP, NULL);
\r
4791 case IDM_HELPCONTENTS:
\r
4792 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4793 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4794 MessageBox (GetFocus(),
\r
4795 "Unable to activate help",
\r
4796 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4800 case IDM_HELPSEARCH:
\r
4801 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4802 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4803 MessageBox (GetFocus(),
\r
4804 "Unable to activate help",
\r
4805 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4809 case IDM_HELPHELP:
\r
4810 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4811 MessageBox (GetFocus(),
\r
4812 "Unable to activate help",
\r
4813 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4818 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4820 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4821 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4822 FreeProcInstance(lpProc);
\r
4825 case IDM_DirectCommand1:
\r
4826 AskQuestionEvent("Direct Command",
\r
4827 "Send to chess program:", "", "1");
\r
4829 case IDM_DirectCommand2:
\r
4830 AskQuestionEvent("Direct Command",
\r
4831 "Send to second chess program:", "", "2");
\r
4834 case EP_WhitePawn:
\r
4835 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4836 fromX = fromY = -1;
\r
4839 case EP_WhiteKnight:
\r
4840 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4841 fromX = fromY = -1;
\r
4844 case EP_WhiteBishop:
\r
4845 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4846 fromX = fromY = -1;
\r
4849 case EP_WhiteRook:
\r
4850 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4851 fromX = fromY = -1;
\r
4854 case EP_WhiteQueen:
\r
4855 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4856 fromX = fromY = -1;
\r
4859 case EP_WhiteFerz:
\r
4860 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4861 fromX = fromY = -1;
\r
4864 case EP_WhiteWazir:
\r
4865 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4866 fromX = fromY = -1;
\r
4869 case EP_WhiteAlfil:
\r
4870 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4871 fromX = fromY = -1;
\r
4874 case EP_WhiteCannon:
\r
4875 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4876 fromX = fromY = -1;
\r
4879 case EP_WhiteCardinal:
\r
4880 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4881 fromX = fromY = -1;
\r
4884 case EP_WhiteMarshall:
\r
4885 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4886 fromX = fromY = -1;
\r
4889 case EP_WhiteKing:
\r
4890 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4891 fromX = fromY = -1;
\r
4894 case EP_BlackPawn:
\r
4895 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4896 fromX = fromY = -1;
\r
4899 case EP_BlackKnight:
\r
4900 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4901 fromX = fromY = -1;
\r
4904 case EP_BlackBishop:
\r
4905 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4906 fromX = fromY = -1;
\r
4909 case EP_BlackRook:
\r
4910 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4911 fromX = fromY = -1;
\r
4914 case EP_BlackQueen:
\r
4915 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4916 fromX = fromY = -1;
\r
4919 case EP_BlackFerz:
\r
4920 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4921 fromX = fromY = -1;
\r
4924 case EP_BlackWazir:
\r
4925 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4926 fromX = fromY = -1;
\r
4929 case EP_BlackAlfil:
\r
4930 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4931 fromX = fromY = -1;
\r
4934 case EP_BlackCannon:
\r
4935 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4936 fromX = fromY = -1;
\r
4939 case EP_BlackCardinal:
\r
4940 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4941 fromX = fromY = -1;
\r
4944 case EP_BlackMarshall:
\r
4945 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4946 fromX = fromY = -1;
\r
4949 case EP_BlackKing:
\r
4950 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4951 fromX = fromY = -1;
\r
4954 case EP_EmptySquare:
\r
4955 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4956 fromX = fromY = -1;
\r
4959 case EP_ClearBoard:
\r
4960 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4961 fromX = fromY = -1;
\r
4965 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4966 fromX = fromY = -1;
\r
4970 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4971 fromX = fromY = -1;
\r
4975 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4976 fromX = fromY = -1;
\r
4980 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4981 fromX = fromY = -1;
\r
4985 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4986 fromX = fromY = -1;
\r
4990 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4991 fromX = fromY = -1;
\r
4995 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4996 fromX = fromY = -1;
\r
5000 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5001 fromX = fromY = -1;
\r
5005 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5006 fromX = fromY = -1;
\r
5010 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5016 case CLOCK_TIMER_ID:
\r
5017 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5018 clockTimerEvent = 0;
\r
5019 DecrementClocks(); /* call into back end */
\r
5021 case LOAD_GAME_TIMER_ID:
\r
5022 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5023 loadGameTimerEvent = 0;
\r
5024 AutoPlayGameLoop(); /* call into back end */
\r
5026 case ANALYSIS_TIMER_ID:
\r
5027 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5028 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5029 AnalysisPeriodicEvent(0);
\r
5031 KillTimer(hwnd, analysisTimerEvent);
\r
5032 analysisTimerEvent = 0;
\r
5035 case DELAYED_TIMER_ID:
\r
5036 KillTimer(hwnd, delayedTimerEvent);
\r
5037 delayedTimerEvent = 0;
\r
5038 delayedTimerCallback();
\r
5043 case WM_USER_Input:
\r
5044 InputEvent(hwnd, message, wParam, lParam);
\r
5047 /* [AS] Also move "attached" child windows */
\r
5048 case WM_WINDOWPOSCHANGING:
\r
5050 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5051 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5053 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5054 /* Window is moving */
\r
5057 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5058 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5059 rcMain.right = wpMain.x + wpMain.width;
\r
5060 rcMain.top = wpMain.y;
\r
5061 rcMain.bottom = wpMain.y + wpMain.height;
\r
5063 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5064 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5065 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5066 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5067 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5068 wpMain.x = lpwp->x;
\r
5069 wpMain.y = lpwp->y;
\r
5074 /* [AS] Snapping */
\r
5075 case WM_ENTERSIZEMOVE:
\r
5076 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5077 if (hwnd == hwndMain) {
\r
5078 doingSizing = TRUE;
\r
5081 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5085 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5086 if (hwnd == hwndMain) {
\r
5087 lastSizing = wParam;
\r
5092 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5093 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5095 case WM_EXITSIZEMOVE:
\r
5096 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5097 if (hwnd == hwndMain) {
\r
5099 doingSizing = FALSE;
\r
5100 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5101 GetClientRect(hwnd, &client);
\r
5102 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5104 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5106 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5109 case WM_DESTROY: /* message: window being destroyed */
\r
5110 PostQuitMessage(0);
\r
5114 if (hwnd == hwndMain) {
\r
5119 default: /* Passes it on if unprocessed */
\r
5120 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5125 /*---------------------------------------------------------------------------*\
\r
5127 * Misc utility routines
\r
5129 \*---------------------------------------------------------------------------*/
\r
5132 * Decent random number generator, at least not as bad as Windows
\r
5133 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5135 unsigned int randstate;
\r
5140 randstate = randstate * 1664525 + 1013904223;
\r
5141 return (int) randstate & 0x7fffffff;
\r
5145 mysrandom(unsigned int seed)
\r
5152 * returns TRUE if user selects a different color, FALSE otherwise
\r
5156 ChangeColor(HWND hwnd, COLORREF *which)
\r
5158 static BOOL firstTime = TRUE;
\r
5159 static DWORD customColors[16];
\r
5161 COLORREF newcolor;
\r
5166 /* Make initial colors in use available as custom colors */
\r
5167 /* Should we put the compiled-in defaults here instead? */
\r
5169 customColors[i++] = lightSquareColor & 0xffffff;
\r
5170 customColors[i++] = darkSquareColor & 0xffffff;
\r
5171 customColors[i++] = whitePieceColor & 0xffffff;
\r
5172 customColors[i++] = blackPieceColor & 0xffffff;
\r
5173 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5174 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5176 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5177 customColors[i++] = textAttribs[ccl].color;
\r
5179 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5180 firstTime = FALSE;
\r
5183 cc.lStructSize = sizeof(cc);
\r
5184 cc.hwndOwner = hwnd;
\r
5185 cc.hInstance = NULL;
\r
5186 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5187 cc.lpCustColors = (LPDWORD) customColors;
\r
5188 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5190 if (!ChooseColor(&cc)) return FALSE;
\r
5192 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5193 if (newcolor == *which) return FALSE;
\r
5194 *which = newcolor;
\r
5198 InitDrawingColors();
\r
5199 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5204 MyLoadSound(MySound *ms)
\r
5210 if (ms->data) free(ms->data);
\r
5213 switch (ms->name[0]) {
\r
5219 /* System sound from Control Panel. Don't preload here. */
\r
5223 if (ms->name[1] == NULLCHAR) {
\r
5224 /* "!" alone = silence */
\r
5227 /* Builtin wave resource. Error if not found. */
\r
5228 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5229 if (h == NULL) break;
\r
5230 ms->data = (void *)LoadResource(hInst, h);
\r
5231 if (h == NULL) break;
\r
5236 /* .wav file. Error if not found. */
\r
5237 f = fopen(ms->name, "rb");
\r
5238 if (f == NULL) break;
\r
5239 if (fstat(fileno(f), &st) < 0) break;
\r
5240 ms->data = malloc(st.st_size);
\r
5241 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5247 char buf[MSG_SIZ];
\r
5248 sprintf(buf, "Error loading sound %s", ms->name);
\r
5249 DisplayError(buf, GetLastError());
\r
5255 MyPlaySound(MySound *ms)
\r
5257 BOOLEAN ok = FALSE;
\r
5259 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5260 switch (ms->name[0]) {
\r
5262 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5267 /* System sound from Control Panel (deprecated feature).
\r
5268 "$" alone or an unset sound name gets default beep (still in use). */
\r
5269 if (ms->name[1]) {
\r
5270 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5272 if (!ok) ok = MessageBeep(MB_OK);
\r
5275 /* Builtin wave resource, or "!" alone for silence */
\r
5276 if (ms->name[1]) {
\r
5277 if (ms->data == NULL) return FALSE;
\r
5278 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5284 /* .wav file. Error if not found. */
\r
5285 if (ms->data == NULL) return FALSE;
\r
5286 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5289 /* Don't print an error: this can happen innocently if the sound driver
\r
5290 is busy; for instance, if another instance of WinBoard is playing
\r
5291 a sound at about the same time. */
\r
5297 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5300 OPENFILENAME *ofn;
\r
5301 static UINT *number; /* gross that this is static */
\r
5303 switch (message) {
\r
5304 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5305 /* Center the dialog over the application window */
\r
5306 ofn = (OPENFILENAME *) lParam;
\r
5307 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5308 number = (UINT *) ofn->lCustData;
\r
5309 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5313 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5314 return FALSE; /* Allow for further processing */
\r
5317 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5318 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5320 return FALSE; /* Allow for further processing */
\r
5326 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5328 static UINT *number;
\r
5329 OPENFILENAME *ofname;
\r
5332 case WM_INITDIALOG:
\r
5333 ofname = (OPENFILENAME *)lParam;
\r
5334 number = (UINT *)(ofname->lCustData);
\r
5337 ofnot = (OFNOTIFY *)lParam;
\r
5338 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5339 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5348 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5349 char *nameFilt, char *dlgTitle, UINT *number,
\r
5350 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5352 OPENFILENAME openFileName;
\r
5353 char buf1[MSG_SIZ];
\r
5356 if (fileName == NULL) fileName = buf1;
\r
5357 if (defName == NULL) {
\r
5358 strcpy(fileName, "*.");
\r
5359 strcat(fileName, defExt);
\r
5361 strcpy(fileName, defName);
\r
5363 if (fileTitle) strcpy(fileTitle, "");
\r
5364 if (number) *number = 0;
\r
5366 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5367 openFileName.hwndOwner = hwnd;
\r
5368 openFileName.hInstance = (HANDLE) hInst;
\r
5369 openFileName.lpstrFilter = nameFilt;
\r
5370 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5371 openFileName.nMaxCustFilter = 0L;
\r
5372 openFileName.nFilterIndex = 1L;
\r
5373 openFileName.lpstrFile = fileName;
\r
5374 openFileName.nMaxFile = MSG_SIZ;
\r
5375 openFileName.lpstrFileTitle = fileTitle;
\r
5376 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5377 openFileName.lpstrInitialDir = NULL;
\r
5378 openFileName.lpstrTitle = dlgTitle;
\r
5379 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5380 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5381 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5382 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5383 openFileName.nFileOffset = 0;
\r
5384 openFileName.nFileExtension = 0;
\r
5385 openFileName.lpstrDefExt = defExt;
\r
5386 openFileName.lCustData = (LONG) number;
\r
5387 openFileName.lpfnHook = oldDialog ?
\r
5388 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5389 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5391 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5392 GetOpenFileName(&openFileName)) {
\r
5393 /* open the file */
\r
5394 f = fopen(openFileName.lpstrFile, write);
\r
5396 MessageBox(hwnd, "File open failed", NULL,
\r
5397 MB_OK|MB_ICONEXCLAMATION);
\r
5401 int err = CommDlgExtendedError();
\r
5402 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5411 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5413 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5416 * Get the first pop-up menu in the menu template. This is the
\r
5417 * menu that TrackPopupMenu displays.
\r
5419 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5421 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5424 * TrackPopup uses screen coordinates, so convert the
\r
5425 * coordinates of the mouse click to screen coordinates.
\r
5427 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5429 /* Draw and track the floating pop-up menu. */
\r
5430 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5431 pt.x, pt.y, 0, hwnd, NULL);
\r
5433 /* Destroy the menu.*/
\r
5434 DestroyMenu(hmenu);
\r
5439 int sizeX, sizeY, newSizeX, newSizeY;
\r
5441 } ResizeEditPlusButtonsClosure;
\r
5444 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5446 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5450 if (hChild == cl->hText) return TRUE;
\r
5451 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5452 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5453 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5454 ScreenToClient(cl->hDlg, &pt);
\r
5455 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5456 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5460 /* Resize a dialog that has a (rich) edit field filling most of
\r
5461 the top, with a row of buttons below */
\r
5463 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5466 int newTextHeight, newTextWidth;
\r
5467 ResizeEditPlusButtonsClosure cl;
\r
5469 /*if (IsIconic(hDlg)) return;*/
\r
5470 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5472 cl.hdwp = BeginDeferWindowPos(8);
\r
5474 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5475 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5476 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5477 if (newTextHeight < 0) {
\r
5478 newSizeY += -newTextHeight;
\r
5479 newTextHeight = 0;
\r
5481 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5482 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5488 cl.newSizeX = newSizeX;
\r
5489 cl.newSizeY = newSizeY;
\r
5490 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5492 EndDeferWindowPos(cl.hdwp);
\r
5495 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5497 RECT rChild, rParent;
\r
5498 int wChild, hChild, wParent, hParent;
\r
5499 int wScreen, hScreen, xNew, yNew;
\r
5502 /* Get the Height and Width of the child window */
\r
5503 GetWindowRect (hwndChild, &rChild);
\r
5504 wChild = rChild.right - rChild.left;
\r
5505 hChild = rChild.bottom - rChild.top;
\r
5507 /* Get the Height and Width of the parent window */
\r
5508 GetWindowRect (hwndParent, &rParent);
\r
5509 wParent = rParent.right - rParent.left;
\r
5510 hParent = rParent.bottom - rParent.top;
\r
5512 /* Get the display limits */
\r
5513 hdc = GetDC (hwndChild);
\r
5514 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5515 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5516 ReleaseDC(hwndChild, hdc);
\r
5518 /* Calculate new X position, then adjust for screen */
\r
5519 xNew = rParent.left + ((wParent - wChild) /2);
\r
5522 } else if ((xNew+wChild) > wScreen) {
\r
5523 xNew = wScreen - wChild;
\r
5526 /* Calculate new Y position, then adjust for screen */
\r
5528 yNew = rParent.top + ((hParent - hChild) /2);
\r
5531 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5536 } else if ((yNew+hChild) > hScreen) {
\r
5537 yNew = hScreen - hChild;
\r
5540 /* Set it, and return */
\r
5541 return SetWindowPos (hwndChild, NULL,
\r
5542 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5545 /* Center one window over another */
\r
5546 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5548 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5551 /*---------------------------------------------------------------------------*\
\r
5553 * Startup Dialog functions
\r
5555 \*---------------------------------------------------------------------------*/
\r
5557 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5559 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5561 while (*cd != NULL) {
\r
5562 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5568 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5570 char buf1[MAX_ARG_LEN];
\r
5573 if (str[0] == '@') {
\r
5574 FILE* f = fopen(str + 1, "r");
\r
5576 DisplayFatalError(str + 1, errno, 2);
\r
5579 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5581 buf1[len] = NULLCHAR;
\r
5585 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5588 char buf[MSG_SIZ];
\r
5589 char *end = strchr(str, '\n');
\r
5590 if (end == NULL) return;
\r
5591 memcpy(buf, str, end - str);
\r
5592 buf[end - str] = NULLCHAR;
\r
5593 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5599 SetStartupDialogEnables(HWND hDlg)
\r
5601 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5602 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5603 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5604 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5605 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5606 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5607 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5608 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5609 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5610 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5611 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5612 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5613 IsDlgButtonChecked(hDlg, OPT_View));
\r
5617 QuoteForFilename(char *filename)
\r
5619 int dquote, space;
\r
5620 dquote = strchr(filename, '"') != NULL;
\r
5621 space = strchr(filename, ' ') != NULL;
\r
5622 if (dquote || space) {
\r
5634 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5636 char buf[MSG_SIZ];
\r
5639 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5640 q = QuoteForFilename(nthcp);
\r
5641 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5642 if (*nthdir != NULLCHAR) {
\r
5643 q = QuoteForFilename(nthdir);
\r
5644 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5646 if (*nthcp == NULLCHAR) {
\r
5647 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5648 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5649 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5650 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5655 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5657 char buf[MSG_SIZ];
\r
5661 switch (message) {
\r
5662 case WM_INITDIALOG:
\r
5663 /* Center the dialog */
\r
5664 CenterWindow (hDlg, GetDesktopWindow());
\r
5665 /* Initialize the dialog items */
\r
5666 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5667 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5668 firstChessProgramNames);
\r
5669 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5670 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5671 secondChessProgramNames);
\r
5672 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5673 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5674 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5675 if (*appData.icsHelper != NULLCHAR) {
\r
5676 char *q = QuoteForFilename(appData.icsHelper);
\r
5677 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5679 if (*appData.icsHost == NULLCHAR) {
\r
5680 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5681 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5682 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5683 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5684 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5687 if (appData.icsActive) {
\r
5688 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5690 else if (appData.noChessProgram) {
\r
5691 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5694 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5697 SetStartupDialogEnables(hDlg);
\r
5701 switch (LOWORD(wParam)) {
\r
5703 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5704 strcpy(buf, "/fcp=");
\r
5705 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5707 ParseArgs(StringGet, &p);
\r
5708 strcpy(buf, "/scp=");
\r
5709 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5711 ParseArgs(StringGet, &p);
\r
5712 appData.noChessProgram = FALSE;
\r
5713 appData.icsActive = FALSE;
\r
5714 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5715 strcpy(buf, "/ics /icshost=");
\r
5716 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5718 ParseArgs(StringGet, &p);
\r
5719 if (appData.zippyPlay) {
\r
5720 strcpy(buf, "/fcp=");
\r
5721 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5723 ParseArgs(StringGet, &p);
\r
5725 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5726 appData.noChessProgram = TRUE;
\r
5727 appData.icsActive = FALSE;
\r
5729 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5730 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5733 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5734 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5736 ParseArgs(StringGet, &p);
\r
5738 EndDialog(hDlg, TRUE);
\r
5745 case IDM_HELPCONTENTS:
\r
5746 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5747 MessageBox (GetFocus(),
\r
5748 "Unable to activate help",
\r
5749 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5754 SetStartupDialogEnables(hDlg);
\r
5762 /*---------------------------------------------------------------------------*\
\r
5764 * About box dialog functions
\r
5766 \*---------------------------------------------------------------------------*/
\r
5768 /* Process messages for "About" dialog box */
\r
5770 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5772 switch (message) {
\r
5773 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5774 /* Center the dialog over the application window */
\r
5775 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5776 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5780 case WM_COMMAND: /* message: received a command */
\r
5781 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5782 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5783 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5791 /*---------------------------------------------------------------------------*\
\r
5793 * Comment Dialog functions
\r
5795 \*---------------------------------------------------------------------------*/
\r
5798 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5800 static HANDLE hwndText = NULL;
\r
5801 int len, newSizeX, newSizeY, flags;
\r
5802 static int sizeX, sizeY;
\r
5807 switch (message) {
\r
5808 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5809 /* Initialize the dialog items */
\r
5810 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5811 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5812 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5813 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5814 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5815 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5816 SetWindowText(hDlg, commentTitle);
\r
5817 if (editComment) {
\r
5818 SetFocus(hwndText);
\r
5820 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5822 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5823 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5824 MAKELPARAM(FALSE, 0));
\r
5825 /* Size and position the dialog */
\r
5826 if (!commentDialog) {
\r
5827 commentDialog = hDlg;
\r
5828 flags = SWP_NOZORDER;
\r
5829 GetClientRect(hDlg, &rect);
\r
5830 sizeX = rect.right;
\r
5831 sizeY = rect.bottom;
\r
5832 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5833 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5834 WINDOWPLACEMENT wp;
\r
5835 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5836 wp.length = sizeof(WINDOWPLACEMENT);
\r
5838 wp.showCmd = SW_SHOW;
\r
5839 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5840 wp.rcNormalPosition.left = wpComment.x;
\r
5841 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5842 wp.rcNormalPosition.top = wpComment.y;
\r
5843 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5844 SetWindowPlacement(hDlg, &wp);
\r
5846 GetClientRect(hDlg, &rect);
\r
5847 newSizeX = rect.right;
\r
5848 newSizeY = rect.bottom;
\r
5849 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5850 newSizeX, newSizeY);
\r
5857 case WM_COMMAND: /* message: received a command */
\r
5858 switch (LOWORD(wParam)) {
\r
5860 if (editComment) {
\r
5862 /* Read changed options from the dialog box */
\r
5863 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5864 len = GetWindowTextLength(hwndText);
\r
5865 str = (char *) malloc(len + 1);
\r
5866 GetWindowText(hwndText, str, len + 1);
\r
5875 ReplaceComment(commentIndex, str);
\r
5882 case OPT_CancelComment:
\r
5886 case OPT_ClearComment:
\r
5887 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5890 case OPT_EditComment:
\r
5891 EditCommentEvent();
\r
5900 newSizeX = LOWORD(lParam);
\r
5901 newSizeY = HIWORD(lParam);
\r
5902 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5907 case WM_GETMINMAXINFO:
\r
5908 /* Prevent resizing window too small */
\r
5909 mmi = (MINMAXINFO *) lParam;
\r
5910 mmi->ptMinTrackSize.x = 100;
\r
5911 mmi->ptMinTrackSize.y = 100;
\r
5918 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5923 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5925 if (str == NULL) str = "";
\r
5926 p = (char *) malloc(2 * strlen(str) + 2);
\r
5929 if (*str == '\n') *q++ = '\r';
\r
5933 if (commentText != NULL) free(commentText);
\r
5935 commentIndex = index;
\r
5936 commentTitle = title;
\r
5938 editComment = edit;
\r
5940 if (commentDialog) {
\r
5941 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5942 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5944 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5945 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5946 hwndMain, (DLGPROC)lpProc);
\r
5947 FreeProcInstance(lpProc);
\r
5953 /*---------------------------------------------------------------------------*\
\r
5955 * Type-in move dialog functions
\r
5957 \*---------------------------------------------------------------------------*/
\r
5960 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5962 char move[MSG_SIZ];
\r
5964 ChessMove moveType;
\r
5965 int fromX, fromY, toX, toY;
\r
5968 switch (message) {
\r
5969 case WM_INITDIALOG:
\r
5970 move[0] = (char) lParam;
\r
5971 move[1] = NULLCHAR;
\r
5972 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5973 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5974 SetWindowText(hInput, move);
\r
5976 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5980 switch (LOWORD(wParam)) {
\r
5982 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5983 { int n; Board board;
\r
5985 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5986 EditPositionPasteFEN(move);
\r
5987 EndDialog(hDlg, TRUE);
\r
5990 // [HGM] movenum: allow move number to be typed in any mode
\r
5991 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5993 EndDialog(hDlg, TRUE);
\r
5997 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5998 gameMode != Training) {
\r
5999 DisplayMoveError("Displayed move is not current");
\r
6001 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6002 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6003 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6004 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6005 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6006 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6007 if (gameMode != Training)
\r
6008 forwardMostMove = currentMove;
\r
6009 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6011 DisplayMoveError("Could not parse move");
\r
6014 EndDialog(hDlg, TRUE);
\r
6017 EndDialog(hDlg, FALSE);
\r
6028 PopUpMoveDialog(char firstchar)
\r
6032 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6033 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6034 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6035 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6036 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6037 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6038 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6039 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6040 gameMode == Training) {
\r
6041 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6042 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6043 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6044 FreeProcInstance(lpProc);
\r
6048 /*---------------------------------------------------------------------------*\
\r
6050 * Type-in name dialog functions
\r
6052 \*---------------------------------------------------------------------------*/
\r
6055 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6057 char move[MSG_SIZ];
\r
6060 switch (message) {
\r
6061 case WM_INITDIALOG:
\r
6062 move[0] = (char) lParam;
\r
6063 move[1] = NULLCHAR;
\r
6064 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6065 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6066 SetWindowText(hInput, move);
\r
6068 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6072 switch (LOWORD(wParam)) {
\r
6074 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6075 appData.userName = strdup(move);
\r
6078 EndDialog(hDlg, TRUE);
\r
6081 EndDialog(hDlg, FALSE);
\r
6092 PopUpNameDialog(char firstchar)
\r
6096 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6097 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6098 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6099 FreeProcInstance(lpProc);
\r
6102 /*---------------------------------------------------------------------------*\
\r
6106 \*---------------------------------------------------------------------------*/
\r
6108 /* Nonmodal error box */
\r
6109 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6110 WPARAM wParam, LPARAM lParam);
\r
6113 ErrorPopUp(char *title, char *content)
\r
6117 BOOLEAN modal = hwndMain == NULL;
\r
6135 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6136 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6139 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6141 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6142 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6143 hwndMain, (DLGPROC)lpProc);
\r
6144 FreeProcInstance(lpProc);
\r
6151 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6152 if (errorDialog == NULL) return;
\r
6153 DestroyWindow(errorDialog);
\r
6154 errorDialog = NULL;
\r
6155 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6159 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6164 switch (message) {
\r
6165 case WM_INITDIALOG:
\r
6166 GetWindowRect(hDlg, &rChild);
\r
6169 SetWindowPos(hDlg, NULL, rChild.left,
\r
6170 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6171 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6175 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6176 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6177 and it doesn't work when you resize the dialog.
\r
6178 For now, just give it a default position.
\r
6180 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6182 errorDialog = hDlg;
\r
6183 SetWindowText(hDlg, errorTitle);
\r
6184 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6185 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6189 switch (LOWORD(wParam)) {
\r
6192 if (errorDialog == hDlg) errorDialog = NULL;
\r
6193 DestroyWindow(hDlg);
\r
6205 HWND gothicDialog = NULL;
\r
6208 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6212 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6214 switch (message) {
\r
6215 case WM_INITDIALOG:
\r
6216 GetWindowRect(hDlg, &rChild);
\r
6218 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6222 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6223 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6224 and it doesn't work when you resize the dialog.
\r
6225 For now, just give it a default position.
\r
6227 gothicDialog = hDlg;
\r
6228 SetWindowText(hDlg, errorTitle);
\r
6229 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6230 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6234 switch (LOWORD(wParam)) {
\r
6237 if (errorDialog == hDlg) errorDialog = NULL;
\r
6238 DestroyWindow(hDlg);
\r
6250 GothicPopUp(char *title, VariantClass variant)
\r
6253 static char *lastTitle;
\r
6255 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6256 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6258 if(lastTitle != title && gothicDialog != NULL) {
\r
6259 DestroyWindow(gothicDialog);
\r
6260 gothicDialog = NULL;
\r
6262 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6263 title = lastTitle;
\r
6264 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6265 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6266 hwndMain, (DLGPROC)lpProc);
\r
6267 FreeProcInstance(lpProc);
\r
6272 /*---------------------------------------------------------------------------*\
\r
6274 * Ics Interaction console functions
\r
6276 \*---------------------------------------------------------------------------*/
\r
6278 #define HISTORY_SIZE 64
\r
6279 static char *history[HISTORY_SIZE];
\r
6280 int histIn = 0, histP = 0;
\r
6283 SaveInHistory(char *cmd)
\r
6285 if (history[histIn] != NULL) {
\r
6286 free(history[histIn]);
\r
6287 history[histIn] = NULL;
\r
6289 if (*cmd == NULLCHAR) return;
\r
6290 history[histIn] = StrSave(cmd);
\r
6291 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6292 if (history[histIn] != NULL) {
\r
6293 free(history[histIn]);
\r
6294 history[histIn] = NULL;
\r
6300 PrevInHistory(char *cmd)
\r
6303 if (histP == histIn) {
\r
6304 if (history[histIn] != NULL) free(history[histIn]);
\r
6305 history[histIn] = StrSave(cmd);
\r
6307 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6308 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6310 return history[histP];
\r
6316 if (histP == histIn) return NULL;
\r
6317 histP = (histP + 1) % HISTORY_SIZE;
\r
6318 return history[histP];
\r
6322 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6326 hmenu = LoadMenu(hInst, "TextMenu");
\r
6327 h = GetSubMenu(hmenu, 0);
\r
6329 if (strcmp(e->item, "-") == 0) {
\r
6330 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6332 if (e->item[0] == '|') {
\r
6333 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
\r
6334 IDM_CommandX + i, &e->item[1]);
\r
6336 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
\r
6345 WNDPROC consoleTextWindowProc;
\r
6348 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6350 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6351 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6355 SetWindowText(hInput, command);
\r
6357 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6359 sel.cpMin = 999999;
\r
6360 sel.cpMax = 999999;
\r
6361 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6366 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6367 if (sel.cpMin == sel.cpMax) {
\r
6368 /* Expand to surrounding word */
\r
6371 tr.chrg.cpMax = sel.cpMin;
\r
6372 tr.chrg.cpMin = --sel.cpMin;
\r
6373 if (sel.cpMin < 0) break;
\r
6374 tr.lpstrText = name;
\r
6375 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6376 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6380 tr.chrg.cpMin = sel.cpMax;
\r
6381 tr.chrg.cpMax = ++sel.cpMax;
\r
6382 tr.lpstrText = name;
\r
6383 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6384 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6387 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6388 MessageBeep(MB_ICONEXCLAMATION);
\r
6392 tr.lpstrText = name;
\r
6393 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6395 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6396 MessageBeep(MB_ICONEXCLAMATION);
\r
6399 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6402 sprintf(buf, "%s %s", command, name);
\r
6403 SetWindowText(hInput, buf);
\r
6404 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6406 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6407 SetWindowText(hInput, buf);
\r
6408 sel.cpMin = 999999;
\r
6409 sel.cpMax = 999999;
\r
6410 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6416 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6421 switch (message) {
\r
6423 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6426 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6429 sel.cpMin = 999999;
\r
6430 sel.cpMax = 999999;
\r
6431 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6432 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6437 if(wParam != '\022') {
\r
6438 if (wParam == '\t') {
\r
6439 if (GetKeyState(VK_SHIFT) < 0) {
\r
6441 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6442 if (buttonDesc[0].hwnd) {
\r
6443 SetFocus(buttonDesc[0].hwnd);
\r
6445 SetFocus(hwndMain);
\r
6449 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6452 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6453 JAWS_DELETE( SetFocus(hInput); )
\r
6454 SendMessage(hInput, message, wParam, lParam);
\r
6457 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6458 case WM_RBUTTONUP:
\r
6459 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6460 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6461 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6464 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6465 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6466 if (sel.cpMin == sel.cpMax) {
\r
6467 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6468 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6470 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6471 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6473 pt.x = LOWORD(lParam);
\r
6474 pt.y = HIWORD(lParam);
\r
6475 MenuPopup(hwnd, pt, hmenu, -1);
\r
6479 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6481 return SendMessage(hInput, message, wParam, lParam);
\r
6482 case WM_MBUTTONDOWN:
\r
6483 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6484 case WM_RBUTTONDOWN:
\r
6485 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6486 /* Move selection here if it was empty */
\r
6488 pt.x = LOWORD(lParam);
\r
6489 pt.y = HIWORD(lParam);
\r
6490 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6491 if (sel.cpMin == sel.cpMax) {
\r
6492 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6493 sel.cpMax = sel.cpMin;
\r
6494 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6496 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6500 switch (LOWORD(wParam)) {
\r
6501 case IDM_QuickPaste:
\r
6503 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6504 if (sel.cpMin == sel.cpMax) {
\r
6505 MessageBeep(MB_ICONEXCLAMATION);
\r
6508 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6509 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6510 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6515 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6518 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6521 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6525 int i = LOWORD(wParam) - IDM_CommandX;
\r
6526 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6527 icsTextMenuEntry[i].command != NULL) {
\r
6528 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6529 icsTextMenuEntry[i].getname,
\r
6530 icsTextMenuEntry[i].immediate);
\r
6538 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6541 WNDPROC consoleInputWindowProc;
\r
6544 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6546 char buf[MSG_SIZ];
\r
6548 static BOOL sendNextChar = FALSE;
\r
6549 static BOOL quoteNextChar = FALSE;
\r
6550 InputSource *is = consoleInputSource;
\r
6554 switch (message) {
\r
6556 if (!appData.localLineEditing || sendNextChar) {
\r
6557 is->buf[0] = (CHAR) wParam;
\r
6559 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6560 sendNextChar = FALSE;
\r
6563 if (quoteNextChar) {
\r
6564 buf[0] = (char) wParam;
\r
6565 buf[1] = NULLCHAR;
\r
6566 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6567 quoteNextChar = FALSE;
\r
6571 case '\r': /* Enter key */
\r
6572 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6573 if (consoleEcho) SaveInHistory(is->buf);
\r
6574 is->buf[is->count++] = '\n';
\r
6575 is->buf[is->count] = NULLCHAR;
\r
6576 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6577 if (consoleEcho) {
\r
6578 ConsoleOutput(is->buf, is->count, TRUE);
\r
6579 } else if (appData.localLineEditing) {
\r
6580 ConsoleOutput("\n", 1, TRUE);
\r
6583 case '\033': /* Escape key */
\r
6584 SetWindowText(hwnd, "");
\r
6585 cf.cbSize = sizeof(CHARFORMAT);
\r
6586 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6587 if (consoleEcho) {
\r
6588 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6590 cf.crTextColor = COLOR_ECHOOFF;
\r
6592 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6593 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6595 case '\t': /* Tab key */
\r
6596 if (GetKeyState(VK_SHIFT) < 0) {
\r
6598 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6601 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6602 if (buttonDesc[0].hwnd) {
\r
6603 SetFocus(buttonDesc[0].hwnd);
\r
6605 SetFocus(hwndMain);
\r
6609 case '\023': /* Ctrl+S */
\r
6610 sendNextChar = TRUE;
\r
6612 case '\021': /* Ctrl+Q */
\r
6613 quoteNextChar = TRUE;
\r
6623 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6624 p = PrevInHistory(buf);
\r
6626 SetWindowText(hwnd, p);
\r
6627 sel.cpMin = 999999;
\r
6628 sel.cpMax = 999999;
\r
6629 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6634 p = NextInHistory();
\r
6636 SetWindowText(hwnd, p);
\r
6637 sel.cpMin = 999999;
\r
6638 sel.cpMax = 999999;
\r
6639 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6645 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6649 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6653 case WM_MBUTTONDOWN:
\r
6654 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6655 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6657 case WM_RBUTTONUP:
\r
6658 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6659 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6660 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6664 hmenu = LoadMenu(hInst, "InputMenu");
\r
6665 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6666 if (sel.cpMin == sel.cpMax) {
\r
6667 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6668 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6670 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6671 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6673 pt.x = LOWORD(lParam);
\r
6674 pt.y = HIWORD(lParam);
\r
6675 MenuPopup(hwnd, pt, hmenu, -1);
\r
6679 switch (LOWORD(wParam)) {
\r
6681 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6683 case IDM_SelectAll:
\r
6685 sel.cpMax = -1; /*999999?*/
\r
6686 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6689 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6692 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6695 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6700 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6703 #define CO_MAX 100000
\r
6704 #define CO_TRIM 1000
\r
6707 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6709 static SnapData sd;
\r
6710 HWND hText, hInput;
\r
6712 static int sizeX, sizeY;
\r
6713 int newSizeX, newSizeY;
\r
6717 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6718 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6720 switch (message) {
\r
6722 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6724 ENLINK *pLink = (ENLINK*)lParam;
\r
6725 if (pLink->msg == WM_LBUTTONUP)
\r
6729 tr.chrg = pLink->chrg;
\r
6730 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6731 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6732 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6733 free(tr.lpstrText);
\r
6737 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6738 hwndConsole = hDlg;
\r
6740 consoleTextWindowProc = (WNDPROC)
\r
6741 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6742 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6743 consoleInputWindowProc = (WNDPROC)
\r
6744 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6745 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6746 Colorize(ColorNormal, TRUE);
\r
6747 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6748 ChangedConsoleFont();
\r
6749 GetClientRect(hDlg, &rect);
\r
6750 sizeX = rect.right;
\r
6751 sizeY = rect.bottom;
\r
6752 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6753 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6754 WINDOWPLACEMENT wp;
\r
6755 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6756 wp.length = sizeof(WINDOWPLACEMENT);
\r
6758 wp.showCmd = SW_SHOW;
\r
6759 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6760 wp.rcNormalPosition.left = wpConsole.x;
\r
6761 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6762 wp.rcNormalPosition.top = wpConsole.y;
\r
6763 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6764 SetWindowPlacement(hDlg, &wp);
\r
6767 // [HGM] Chessknight's change 2004-07-13
\r
6768 else { /* Determine Defaults */
\r
6769 WINDOWPLACEMENT wp;
\r
6770 wpConsole.x = wpMain.width + 1;
\r
6771 wpConsole.y = wpMain.y;
\r
6772 wpConsole.width = screenWidth - wpMain.width;
\r
6773 wpConsole.height = wpMain.height;
\r
6774 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6775 wp.length = sizeof(WINDOWPLACEMENT);
\r
6777 wp.showCmd = SW_SHOW;
\r
6778 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6779 wp.rcNormalPosition.left = wpConsole.x;
\r
6780 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6781 wp.rcNormalPosition.top = wpConsole.y;
\r
6782 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6783 SetWindowPlacement(hDlg, &wp);
\r
6786 // Allow hText to highlight URLs and send notifications on them
\r
6787 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6788 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6789 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6790 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6804 if (IsIconic(hDlg)) break;
\r
6805 newSizeX = LOWORD(lParam);
\r
6806 newSizeY = HIWORD(lParam);
\r
6807 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6808 RECT rectText, rectInput;
\r
6810 int newTextHeight, newTextWidth;
\r
6811 GetWindowRect(hText, &rectText);
\r
6812 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6813 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6814 if (newTextHeight < 0) {
\r
6815 newSizeY += -newTextHeight;
\r
6816 newTextHeight = 0;
\r
6818 SetWindowPos(hText, NULL, 0, 0,
\r
6819 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6820 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6821 pt.x = rectInput.left;
\r
6822 pt.y = rectInput.top + newSizeY - sizeY;
\r
6823 ScreenToClient(hDlg, &pt);
\r
6824 SetWindowPos(hInput, NULL,
\r
6825 pt.x, pt.y, /* needs client coords */
\r
6826 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6827 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6833 case WM_GETMINMAXINFO:
\r
6834 /* Prevent resizing window too small */
\r
6835 mmi = (MINMAXINFO *) lParam;
\r
6836 mmi->ptMinTrackSize.x = 100;
\r
6837 mmi->ptMinTrackSize.y = 100;
\r
6840 /* [AS] Snapping */
\r
6841 case WM_ENTERSIZEMOVE:
\r
6842 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6845 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6848 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6850 case WM_EXITSIZEMOVE:
\r
6851 UpdateICSWidth(hText);
\r
6852 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6855 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6863 if (hwndConsole) return;
\r
6864 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6865 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6870 ConsoleOutput(char* data, int length, int forceVisible)
\r
6875 char buf[CO_MAX+1];
\r
6878 static int delayLF = 0;
\r
6879 CHARRANGE savesel, sel;
\r
6881 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6889 while (length--) {
\r
6897 } else if (*p == '\007') {
\r
6898 MyPlaySound(&sounds[(int)SoundBell]);
\r
6905 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6906 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6907 /* Save current selection */
\r
6908 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6909 exlen = GetWindowTextLength(hText);
\r
6910 /* Find out whether current end of text is visible */
\r
6911 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6912 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6913 /* Trim existing text if it's too long */
\r
6914 if (exlen + (q - buf) > CO_MAX) {
\r
6915 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6918 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6919 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6921 savesel.cpMin -= trim;
\r
6922 savesel.cpMax -= trim;
\r
6923 if (exlen < 0) exlen = 0;
\r
6924 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6925 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6927 /* Append the new text */
\r
6928 sel.cpMin = exlen;
\r
6929 sel.cpMax = exlen;
\r
6930 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6931 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6932 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6933 if (forceVisible || exlen == 0 ||
\r
6934 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6935 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6936 /* Scroll to make new end of text visible if old end of text
\r
6937 was visible or new text is an echo of user typein */
\r
6938 sel.cpMin = 9999999;
\r
6939 sel.cpMax = 9999999;
\r
6940 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6941 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6942 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6943 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6945 if (savesel.cpMax == exlen || forceVisible) {
\r
6946 /* Move insert point to new end of text if it was at the old
\r
6947 end of text or if the new text is an echo of user typein */
\r
6948 sel.cpMin = 9999999;
\r
6949 sel.cpMax = 9999999;
\r
6950 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6952 /* Restore previous selection */
\r
6953 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6955 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6962 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6966 COLORREF oldFg, oldBg;
\r
6970 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6972 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6973 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6974 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6977 rect.right = x + squareSize;
\r
6979 rect.bottom = y + squareSize;
\r
6982 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6983 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6984 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6985 &rect, str, strlen(str), NULL);
\r
6987 (void) SetTextColor(hdc, oldFg);
\r
6988 (void) SetBkColor(hdc, oldBg);
\r
6989 (void) SelectObject(hdc, oldFont);
\r
6993 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6994 RECT *rect, char *color, char *flagFell)
\r
6998 COLORREF oldFg, oldBg;
\r
7001 if (appData.clockMode) {
\r
7003 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7005 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7012 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7013 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7015 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7016 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7018 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7022 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7023 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7024 rect, str, strlen(str), NULL);
\r
7025 if(logoHeight > 0 && appData.clockMode) {
\r
7027 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7028 r.top = rect->top + logoHeight/2;
\r
7029 r.left = rect->left;
\r
7030 r.right = rect->right;
\r
7031 r.bottom = rect->bottom;
\r
7032 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7033 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7034 &r, str, strlen(str), NULL);
\r
7036 (void) SetTextColor(hdc, oldFg);
\r
7037 (void) SetBkColor(hdc, oldBg);
\r
7038 (void) SelectObject(hdc, oldFont);
\r
7043 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7049 if( count <= 0 ) {
\r
7050 if (appData.debugMode) {
\r
7051 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7054 return ERROR_INVALID_USER_BUFFER;
\r
7057 ResetEvent(ovl->hEvent);
\r
7058 ovl->Offset = ovl->OffsetHigh = 0;
\r
7059 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7063 err = GetLastError();
\r
7064 if (err == ERROR_IO_PENDING) {
\r
7065 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7069 err = GetLastError();
\r
7076 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7081 ResetEvent(ovl->hEvent);
\r
7082 ovl->Offset = ovl->OffsetHigh = 0;
\r
7083 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7087 err = GetLastError();
\r
7088 if (err == ERROR_IO_PENDING) {
\r
7089 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7093 err = GetLastError();
\r
7099 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7100 void CheckForInputBufferFull( InputSource * is )
\r
7102 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7103 /* Look for end of line */
\r
7104 char * p = is->buf;
\r
7106 while( p < is->next && *p != '\n' ) {
\r
7110 if( p >= is->next ) {
\r
7111 if (appData.debugMode) {
\r
7112 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7115 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7116 is->count = (DWORD) -1;
\r
7117 is->next = is->buf;
\r
7123 InputThread(LPVOID arg)
\r
7128 is = (InputSource *) arg;
\r
7129 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7130 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7131 while (is->hThread != NULL) {
\r
7132 is->error = DoReadFile(is->hFile, is->next,
\r
7133 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7134 &is->count, &ovl);
\r
7135 if (is->error == NO_ERROR) {
\r
7136 is->next += is->count;
\r
7138 if (is->error == ERROR_BROKEN_PIPE) {
\r
7139 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7142 is->count = (DWORD) -1;
\r
7143 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7148 CheckForInputBufferFull( is );
\r
7150 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7152 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7154 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7157 CloseHandle(ovl.hEvent);
\r
7158 CloseHandle(is->hFile);
\r
7160 if (appData.debugMode) {
\r
7161 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7168 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7170 NonOvlInputThread(LPVOID arg)
\r
7177 is = (InputSource *) arg;
\r
7178 while (is->hThread != NULL) {
\r
7179 is->error = ReadFile(is->hFile, is->next,
\r
7180 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7181 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7182 if (is->error == NO_ERROR) {
\r
7183 /* Change CRLF to LF */
\r
7184 if (is->next > is->buf) {
\r
7186 i = is->count + 1;
\r
7194 if (prev == '\r' && *p == '\n') {
\r
7206 if (is->error == ERROR_BROKEN_PIPE) {
\r
7207 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7210 is->count = (DWORD) -1;
\r
7214 CheckForInputBufferFull( is );
\r
7216 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7218 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7220 if (is->count < 0) break; /* Quit on error */
\r
7222 CloseHandle(is->hFile);
\r
7227 SocketInputThread(LPVOID arg)
\r
7231 is = (InputSource *) arg;
\r
7232 while (is->hThread != NULL) {
\r
7233 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7234 if ((int)is->count == SOCKET_ERROR) {
\r
7235 is->count = (DWORD) -1;
\r
7236 is->error = WSAGetLastError();
\r
7238 is->error = NO_ERROR;
\r
7239 is->next += is->count;
\r
7240 if (is->count == 0 && is->second == is) {
\r
7241 /* End of file on stderr; quit with no message */
\r
7245 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7247 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7249 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7255 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7259 is = (InputSource *) lParam;
\r
7260 if (is->lineByLine) {
\r
7261 /* Feed in lines one by one */
\r
7262 char *p = is->buf;
\r
7264 while (q < is->next) {
\r
7265 if (*q++ == '\n') {
\r
7266 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7271 /* Move any partial line to the start of the buffer */
\r
7273 while (p < is->next) {
\r
7278 if (is->error != NO_ERROR || is->count == 0) {
\r
7279 /* Notify backend of the error. Note: If there was a partial
\r
7280 line at the end, it is not flushed through. */
\r
7281 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7284 /* Feed in the whole chunk of input at once */
\r
7285 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7286 is->next = is->buf;
\r
7290 /*---------------------------------------------------------------------------*\
\r
7292 * Menu enables. Used when setting various modes.
\r
7294 \*---------------------------------------------------------------------------*/
\r
7302 GreyRevert(Boolean grey)
\r
7303 { // [HGM] vari: for retracting variations in local mode
\r
7304 HMENU hmenu = GetMenu(hwndMain);
\r
7305 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7309 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7311 while (enab->item > 0) {
\r
7312 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7317 Enables gnuEnables[] = {
\r
7318 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7319 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7320 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7321 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7322 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7323 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7324 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7325 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7326 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7327 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7328 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7332 Enables icsEnables[] = {
\r
7333 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7334 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7335 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7336 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7337 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7338 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7339 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7340 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7341 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7342 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7343 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7344 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7345 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7346 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7347 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7352 Enables zippyEnables[] = {
\r
7353 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7354 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7355 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7356 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7361 Enables ncpEnables[] = {
\r
7362 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7363 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7364 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7365 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7366 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7367 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7368 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7369 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7370 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7371 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7372 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7373 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7374 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7375 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7376 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7377 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7378 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7379 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7380 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7384 Enables trainingOnEnables[] = {
\r
7385 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7386 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7387 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7388 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7389 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7390 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7391 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7392 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7396 Enables trainingOffEnables[] = {
\r
7397 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7398 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7399 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7400 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7401 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7402 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7403 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7404 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7408 /* These modify either ncpEnables or gnuEnables */
\r
7409 Enables cmailEnables[] = {
\r
7410 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7411 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7412 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7413 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7415 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7416 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7420 Enables machineThinkingEnables[] = {
\r
7421 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7422 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7424 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7425 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7426 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7427 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7428 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7429 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7439 Enables userThinkingEnables[] = {
\r
7440 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7441 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7442 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7443 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7444 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7445 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7446 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7447 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7448 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7449 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7450 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7451 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7452 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7453 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7454 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7458 /*---------------------------------------------------------------------------*\
\r
7460 * Front-end interface functions exported by XBoard.
\r
7461 * Functions appear in same order as prototypes in frontend.h.
\r
7463 \*---------------------------------------------------------------------------*/
\r
7467 static UINT prevChecked = 0;
\r
7468 static int prevPausing = 0;
\r
7471 if (pausing != prevPausing) {
\r
7472 prevPausing = pausing;
\r
7473 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7474 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7475 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7478 switch (gameMode) {
\r
7479 case BeginningOfGame:
\r
7480 if (appData.icsActive)
\r
7481 nowChecked = IDM_IcsClient;
\r
7482 else if (appData.noChessProgram)
\r
7483 nowChecked = IDM_EditGame;
\r
7485 nowChecked = IDM_MachineBlack;
\r
7487 case MachinePlaysBlack:
\r
7488 nowChecked = IDM_MachineBlack;
\r
7490 case MachinePlaysWhite:
\r
7491 nowChecked = IDM_MachineWhite;
\r
7493 case TwoMachinesPlay:
\r
7494 nowChecked = IDM_TwoMachines;
\r
7497 nowChecked = IDM_AnalysisMode;
\r
7500 nowChecked = IDM_AnalyzeFile;
\r
7503 nowChecked = IDM_EditGame;
\r
7505 case PlayFromGameFile:
\r
7506 nowChecked = IDM_LoadGame;
\r
7508 case EditPosition:
\r
7509 nowChecked = IDM_EditPosition;
\r
7512 nowChecked = IDM_Training;
\r
7514 case IcsPlayingWhite:
\r
7515 case IcsPlayingBlack:
\r
7516 case IcsObserving:
\r
7518 nowChecked = IDM_IcsClient;
\r
7525 if (prevChecked != 0)
\r
7526 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7527 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7528 if (nowChecked != 0)
\r
7529 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7530 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7532 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7533 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7534 MF_BYCOMMAND|MF_ENABLED);
\r
7536 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7537 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7540 prevChecked = nowChecked;
\r
7542 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7543 if (appData.icsActive) {
\r
7544 if (appData.icsEngineAnalyze) {
\r
7545 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7546 MF_BYCOMMAND|MF_CHECKED);
\r
7548 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7549 MF_BYCOMMAND|MF_UNCHECKED);
\r
7557 HMENU hmenu = GetMenu(hwndMain);
\r
7558 SetMenuEnables(hmenu, icsEnables);
\r
7559 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7560 MF_BYPOSITION|MF_ENABLED);
\r
7562 if (appData.zippyPlay) {
\r
7563 SetMenuEnables(hmenu, zippyEnables);
\r
7564 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7565 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7566 MF_BYCOMMAND|MF_ENABLED);
\r
7574 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7580 HMENU hmenu = GetMenu(hwndMain);
\r
7581 SetMenuEnables(hmenu, ncpEnables);
\r
7582 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7583 MF_BYPOSITION|MF_GRAYED);
\r
7584 DrawMenuBar(hwndMain);
\r
7590 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7594 SetTrainingModeOn()
\r
7597 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7598 for (i = 0; i < N_BUTTONS; i++) {
\r
7599 if (buttonDesc[i].hwnd != NULL)
\r
7600 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7605 VOID SetTrainingModeOff()
\r
7608 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7609 for (i = 0; i < N_BUTTONS; i++) {
\r
7610 if (buttonDesc[i].hwnd != NULL)
\r
7611 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7617 SetUserThinkingEnables()
\r
7619 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7623 SetMachineThinkingEnables()
\r
7625 HMENU hMenu = GetMenu(hwndMain);
\r
7626 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7628 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7630 if (gameMode == MachinePlaysBlack) {
\r
7631 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7632 } else if (gameMode == MachinePlaysWhite) {
\r
7633 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7634 } else if (gameMode == TwoMachinesPlay) {
\r
7635 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7641 DisplayTitle(char *str)
\r
7643 char title[MSG_SIZ], *host;
\r
7644 if (str[0] != NULLCHAR) {
\r
7645 strcpy(title, str);
\r
7646 } else if (appData.icsActive) {
\r
7647 if (appData.icsCommPort[0] != NULLCHAR)
\r
7650 host = appData.icsHost;
\r
7651 sprintf(title, "%s: %s", szTitle, host);
\r
7652 } else if (appData.noChessProgram) {
\r
7653 strcpy(title, szTitle);
\r
7655 strcpy(title, szTitle);
\r
7656 strcat(title, ": ");
\r
7657 strcat(title, first.tidy);
\r
7659 SetWindowText(hwndMain, title);
\r
7664 DisplayMessage(char *str1, char *str2)
\r
7668 int remain = MESSAGE_TEXT_MAX - 1;
\r
7671 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7672 messageText[0] = NULLCHAR;
\r
7674 len = strlen(str1);
\r
7675 if (len > remain) len = remain;
\r
7676 strncpy(messageText, str1, len);
\r
7677 messageText[len] = NULLCHAR;
\r
7680 if (*str2 && remain >= 2) {
\r
7682 strcat(messageText, " ");
\r
7685 len = strlen(str2);
\r
7686 if (len > remain) len = remain;
\r
7687 strncat(messageText, str2, len);
\r
7689 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7691 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7695 hdc = GetDC(hwndMain);
\r
7696 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7697 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7698 &messageRect, messageText, strlen(messageText), NULL);
\r
7699 (void) SelectObject(hdc, oldFont);
\r
7700 (void) ReleaseDC(hwndMain, hdc);
\r
7704 DisplayError(char *str, int error)
\r
7706 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7712 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7713 NULL, error, LANG_NEUTRAL,
\r
7714 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7716 sprintf(buf, "%s:\n%s", str, buf2);
\r
7718 ErrorMap *em = errmap;
\r
7719 while (em->err != 0 && em->err != error) em++;
\r
7720 if (em->err != 0) {
\r
7721 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7723 sprintf(buf, "%s:\nError code %d", str, error);
\r
7728 ErrorPopUp("Error", buf);
\r
7733 DisplayMoveError(char *str)
\r
7735 fromX = fromY = -1;
\r
7736 ClearHighlights();
\r
7737 DrawPosition(FALSE, NULL);
\r
7738 if (appData.popupMoveErrors) {
\r
7739 ErrorPopUp("Error", str);
\r
7741 DisplayMessage(str, "");
\r
7742 moveErrorMessageUp = TRUE;
\r
7747 DisplayFatalError(char *str, int error, int exitStatus)
\r
7749 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7751 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7754 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7755 NULL, error, LANG_NEUTRAL,
\r
7756 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7758 sprintf(buf, "%s:\n%s", str, buf2);
\r
7760 ErrorMap *em = errmap;
\r
7761 while (em->err != 0 && em->err != error) em++;
\r
7762 if (em->err != 0) {
\r
7763 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7765 sprintf(buf, "%s:\nError code %d", str, error);
\r
7770 if (appData.debugMode) {
\r
7771 fprintf(debugFP, "%s: %s\n", label, str);
\r
7773 if (appData.popupExitMessage) {
\r
7774 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7775 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7777 ExitEvent(exitStatus);
\r
7782 DisplayInformation(char *str)
\r
7784 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7789 DisplayNote(char *str)
\r
7791 ErrorPopUp("Note", str);
\r
7796 char *title, *question, *replyPrefix;
\r
7801 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7803 static QuestionParams *qp;
\r
7804 char reply[MSG_SIZ];
\r
7807 switch (message) {
\r
7808 case WM_INITDIALOG:
\r
7809 qp = (QuestionParams *) lParam;
\r
7810 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7811 SetWindowText(hDlg, qp->title);
\r
7812 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7813 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7817 switch (LOWORD(wParam)) {
\r
7819 strcpy(reply, qp->replyPrefix);
\r
7820 if (*reply) strcat(reply, " ");
\r
7821 len = strlen(reply);
\r
7822 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7823 strcat(reply, "\n");
\r
7824 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7825 EndDialog(hDlg, TRUE);
\r
7826 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7829 EndDialog(hDlg, FALSE);
\r
7840 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7842 QuestionParams qp;
\r
7846 qp.question = question;
\r
7847 qp.replyPrefix = replyPrefix;
\r
7849 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7850 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7851 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7852 FreeProcInstance(lpProc);
\r
7855 /* [AS] Pick FRC position */
\r
7856 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7858 static int * lpIndexFRC;
\r
7864 case WM_INITDIALOG:
\r
7865 lpIndexFRC = (int *) lParam;
\r
7867 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7869 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7870 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7871 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7872 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7877 switch( LOWORD(wParam) ) {
\r
7879 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7880 EndDialog( hDlg, 0 );
\r
7881 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7884 EndDialog( hDlg, 1 );
\r
7886 case IDC_NFG_Edit:
\r
7887 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7888 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7890 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7893 case IDC_NFG_Random:
\r
7894 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7895 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7908 int index = appData.defaultFrcPosition;
\r
7909 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7911 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7913 if( result == 0 ) {
\r
7914 appData.defaultFrcPosition = index;
\r
7920 /* [AS] Game list options. Refactored by HGM */
\r
7922 HWND gameListOptionsDialog;
\r
7924 // low-level front-end: clear text edit / list widget
\r
7928 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7931 // low-level front-end: clear text edit / list widget
\r
7933 GLT_DeSelectList()
\r
7935 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7938 // low-level front-end: append line to text edit / list widget
\r
7940 GLT_AddToList( char *name )
\r
7943 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
7947 // low-level front-end: get line from text edit / list widget
\r
7949 GLT_GetFromList( int index, char *name )
\r
7952 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
7958 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7960 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7961 int idx2 = idx1 + delta;
\r
7962 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7964 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7967 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7968 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7969 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7970 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7974 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7978 case WM_INITDIALOG:
\r
7979 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
7981 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7983 /* Initialize list */
\r
7984 GLT_TagsToList( lpUserGLT );
\r
7986 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
7991 switch( LOWORD(wParam) ) {
\r
7994 EndDialog( hDlg, 0 );
\r
7997 EndDialog( hDlg, 1 );
\r
8000 case IDC_GLT_Default:
\r
8001 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8004 case IDC_GLT_Restore:
\r
8005 GLT_TagsToList( appData.gameListTags );
\r
8009 GLT_MoveSelection( hDlg, -1 );
\r
8012 case IDC_GLT_Down:
\r
8013 GLT_MoveSelection( hDlg, +1 );
\r
8023 int GameListOptions()
\r
8026 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8028 strcpy( lpUserGLT, appData.gameListTags );
\r
8030 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8032 if( result == 0 ) {
\r
8033 /* [AS] Memory leak here! */
\r
8034 appData.gameListTags = strdup( lpUserGLT );
\r
8041 DisplayIcsInteractionTitle(char *str)
\r
8043 char consoleTitle[MSG_SIZ];
\r
8045 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8046 SetWindowText(hwndConsole, consoleTitle);
\r
8050 DrawPosition(int fullRedraw, Board board)
\r
8052 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8055 void NotifyFrontendLogin()
\r
8058 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8064 fromX = fromY = -1;
\r
8065 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8066 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8067 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8068 dragInfo.lastpos = dragInfo.pos;
\r
8069 dragInfo.start.x = dragInfo.start.y = -1;
\r
8070 dragInfo.from = dragInfo.start;
\r
8072 DrawPosition(TRUE, NULL);
\r
8078 CommentPopUp(char *title, char *str)
\r
8080 HWND hwnd = GetActiveWindow();
\r
8081 EitherCommentPopUp(0, title, str, FALSE);
\r
8083 SetActiveWindow(hwnd);
\r
8087 CommentPopDown(void)
\r
8089 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8090 if (commentDialog) {
\r
8091 ShowWindow(commentDialog, SW_HIDE);
\r
8093 commentUp = FALSE;
\r
8097 EditCommentPopUp(int index, char *title, char *str)
\r
8099 EitherCommentPopUp(index, title, str, TRUE);
\r
8106 MyPlaySound(&sounds[(int)SoundMove]);
\r
8109 VOID PlayIcsWinSound()
\r
8111 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8114 VOID PlayIcsLossSound()
\r
8116 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8119 VOID PlayIcsDrawSound()
\r
8121 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8124 VOID PlayIcsUnfinishedSound()
\r
8126 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8132 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8140 consoleEcho = TRUE;
\r
8141 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8142 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8143 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8152 consoleEcho = FALSE;
\r
8153 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8154 /* This works OK: set text and background both to the same color */
\r
8156 cf.crTextColor = COLOR_ECHOOFF;
\r
8157 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8158 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8161 /* No Raw()...? */
\r
8163 void Colorize(ColorClass cc, int continuation)
\r
8165 currentColorClass = cc;
\r
8166 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8167 consoleCF.crTextColor = textAttribs[cc].color;
\r
8168 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8169 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8175 static char buf[MSG_SIZ];
\r
8176 DWORD bufsiz = MSG_SIZ;
\r
8178 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8179 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8181 if (!GetUserName(buf, &bufsiz)) {
\r
8182 /*DisplayError("Error getting user name", GetLastError());*/
\r
8183 strcpy(buf, "User");
\r
8191 static char buf[MSG_SIZ];
\r
8192 DWORD bufsiz = MSG_SIZ;
\r
8194 if (!GetComputerName(buf, &bufsiz)) {
\r
8195 /*DisplayError("Error getting host name", GetLastError());*/
\r
8196 strcpy(buf, "Unknown");
\r
8203 ClockTimerRunning()
\r
8205 return clockTimerEvent != 0;
\r
8211 if (clockTimerEvent == 0) return FALSE;
\r
8212 KillTimer(hwndMain, clockTimerEvent);
\r
8213 clockTimerEvent = 0;
\r
8218 StartClockTimer(long millisec)
\r
8220 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8221 (UINT) millisec, NULL);
\r
8225 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8228 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8230 if(appData.noGUI) return;
\r
8231 hdc = GetDC(hwndMain);
\r
8232 if (!IsIconic(hwndMain)) {
\r
8233 DisplayAClock(hdc, timeRemaining, highlight,
\r
8234 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8236 if (highlight && iconCurrent == iconBlack) {
\r
8237 iconCurrent = iconWhite;
\r
8238 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8239 if (IsIconic(hwndMain)) {
\r
8240 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8243 (void) ReleaseDC(hwndMain, hdc);
\r
8245 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8249 DisplayBlackClock(long timeRemaining, int highlight)
\r
8252 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8254 if(appData.noGUI) return;
\r
8255 hdc = GetDC(hwndMain);
\r
8256 if (!IsIconic(hwndMain)) {
\r
8257 DisplayAClock(hdc, timeRemaining, highlight,
\r
8258 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8260 if (highlight && iconCurrent == iconWhite) {
\r
8261 iconCurrent = iconBlack;
\r
8262 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8263 if (IsIconic(hwndMain)) {
\r
8264 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8267 (void) ReleaseDC(hwndMain, hdc);
\r
8269 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8274 LoadGameTimerRunning()
\r
8276 return loadGameTimerEvent != 0;
\r
8280 StopLoadGameTimer()
\r
8282 if (loadGameTimerEvent == 0) return FALSE;
\r
8283 KillTimer(hwndMain, loadGameTimerEvent);
\r
8284 loadGameTimerEvent = 0;
\r
8289 StartLoadGameTimer(long millisec)
\r
8291 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8292 (UINT) millisec, NULL);
\r
8300 char fileTitle[MSG_SIZ];
\r
8302 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8303 f = OpenFileDialog(hwndMain, "a", defName,
\r
8304 appData.oldSaveStyle ? "gam" : "pgn",
\r
8306 "Save Game to File", NULL, fileTitle, NULL);
\r
8308 SaveGame(f, 0, "");
\r
8315 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8317 if (delayedTimerEvent != 0) {
\r
8318 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8319 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8321 KillTimer(hwndMain, delayedTimerEvent);
\r
8322 delayedTimerEvent = 0;
\r
8323 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8324 delayedTimerCallback();
\r
8326 delayedTimerCallback = cb;
\r
8327 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8328 (UINT) millisec, NULL);
\r
8331 DelayedEventCallback
\r
8334 if (delayedTimerEvent) {
\r
8335 return delayedTimerCallback;
\r
8342 CancelDelayedEvent()
\r
8344 if (delayedTimerEvent) {
\r
8345 KillTimer(hwndMain, delayedTimerEvent);
\r
8346 delayedTimerEvent = 0;
\r
8350 DWORD GetWin32Priority(int nice)
\r
8351 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8353 REALTIME_PRIORITY_CLASS 0x00000100
\r
8354 HIGH_PRIORITY_CLASS 0x00000080
\r
8355 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8356 NORMAL_PRIORITY_CLASS 0x00000020
\r
8357 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8358 IDLE_PRIORITY_CLASS 0x00000040
\r
8360 if (nice < -15) return 0x00000080;
\r
8361 if (nice < 0) return 0x00008000;
\r
8362 if (nice == 0) return 0x00000020;
\r
8363 if (nice < 15) return 0x00004000;
\r
8364 return 0x00000040;
\r
8367 /* Start a child process running the given program.
\r
8368 The process's standard output can be read from "from", and its
\r
8369 standard input can be written to "to".
\r
8370 Exit with fatal error if anything goes wrong.
\r
8371 Returns an opaque pointer that can be used to destroy the process
\r
8375 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8377 #define BUFSIZE 4096
\r
8379 HANDLE hChildStdinRd, hChildStdinWr,
\r
8380 hChildStdoutRd, hChildStdoutWr;
\r
8381 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8382 SECURITY_ATTRIBUTES saAttr;
\r
8384 PROCESS_INFORMATION piProcInfo;
\r
8385 STARTUPINFO siStartInfo;
\r
8387 char buf[MSG_SIZ];
\r
8390 if (appData.debugMode) {
\r
8391 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8396 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8397 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8398 saAttr.bInheritHandle = TRUE;
\r
8399 saAttr.lpSecurityDescriptor = NULL;
\r
8402 * The steps for redirecting child's STDOUT:
\r
8403 * 1. Create anonymous pipe to be STDOUT for child.
\r
8404 * 2. Create a noninheritable duplicate of read handle,
\r
8405 * and close the inheritable read handle.
\r
8408 /* Create a pipe for the child's STDOUT. */
\r
8409 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8410 return GetLastError();
\r
8413 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8414 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8415 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8416 FALSE, /* not inherited */
\r
8417 DUPLICATE_SAME_ACCESS);
\r
8419 return GetLastError();
\r
8421 CloseHandle(hChildStdoutRd);
\r
8424 * The steps for redirecting child's STDIN:
\r
8425 * 1. Create anonymous pipe to be STDIN for child.
\r
8426 * 2. Create a noninheritable duplicate of write handle,
\r
8427 * and close the inheritable write handle.
\r
8430 /* Create a pipe for the child's STDIN. */
\r
8431 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8432 return GetLastError();
\r
8435 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8436 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8437 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8438 FALSE, /* not inherited */
\r
8439 DUPLICATE_SAME_ACCESS);
\r
8441 return GetLastError();
\r
8443 CloseHandle(hChildStdinWr);
\r
8445 /* Arrange to (1) look in dir for the child .exe file, and
\r
8446 * (2) have dir be the child's working directory. Interpret
\r
8447 * dir relative to the directory WinBoard loaded from. */
\r
8448 GetCurrentDirectory(MSG_SIZ, buf);
\r
8449 SetCurrentDirectory(installDir);
\r
8450 SetCurrentDirectory(dir);
\r
8452 /* Now create the child process. */
\r
8454 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8455 siStartInfo.lpReserved = NULL;
\r
8456 siStartInfo.lpDesktop = NULL;
\r
8457 siStartInfo.lpTitle = NULL;
\r
8458 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8459 siStartInfo.cbReserved2 = 0;
\r
8460 siStartInfo.lpReserved2 = NULL;
\r
8461 siStartInfo.hStdInput = hChildStdinRd;
\r
8462 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8463 siStartInfo.hStdError = hChildStdoutWr;
\r
8465 fSuccess = CreateProcess(NULL,
\r
8466 cmdLine, /* command line */
\r
8467 NULL, /* process security attributes */
\r
8468 NULL, /* primary thread security attrs */
\r
8469 TRUE, /* handles are inherited */
\r
8470 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8471 NULL, /* use parent's environment */
\r
8473 &siStartInfo, /* STARTUPINFO pointer */
\r
8474 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8476 err = GetLastError();
\r
8477 SetCurrentDirectory(buf); /* return to prev directory */
\r
8482 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8483 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8484 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8487 /* Close the handles we don't need in the parent */
\r
8488 CloseHandle(piProcInfo.hThread);
\r
8489 CloseHandle(hChildStdinRd);
\r
8490 CloseHandle(hChildStdoutWr);
\r
8492 /* Prepare return value */
\r
8493 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8494 cp->kind = CPReal;
\r
8495 cp->hProcess = piProcInfo.hProcess;
\r
8496 cp->pid = piProcInfo.dwProcessId;
\r
8497 cp->hFrom = hChildStdoutRdDup;
\r
8498 cp->hTo = hChildStdinWrDup;
\r
8500 *pr = (void *) cp;
\r
8502 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8503 2000 where engines sometimes don't see the initial command(s)
\r
8504 from WinBoard and hang. I don't understand how that can happen,
\r
8505 but the Sleep is harmless, so I've put it in. Others have also
\r
8506 reported what may be the same problem, so hopefully this will fix
\r
8507 it for them too. */
\r
8515 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8517 ChildProc *cp; int result;
\r
8519 cp = (ChildProc *) pr;
\r
8520 if (cp == NULL) return;
\r
8522 switch (cp->kind) {
\r
8524 /* TerminateProcess is considered harmful, so... */
\r
8525 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8526 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8527 /* The following doesn't work because the chess program
\r
8528 doesn't "have the same console" as WinBoard. Maybe
\r
8529 we could arrange for this even though neither WinBoard
\r
8530 nor the chess program uses a console for stdio? */
\r
8531 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8533 /* [AS] Special termination modes for misbehaving programs... */
\r
8534 if( signal == 9 ) {
\r
8535 result = TerminateProcess( cp->hProcess, 0 );
\r
8537 if ( appData.debugMode) {
\r
8538 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8541 else if( signal == 10 ) {
\r
8542 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8544 if( dw != WAIT_OBJECT_0 ) {
\r
8545 result = TerminateProcess( cp->hProcess, 0 );
\r
8547 if ( appData.debugMode) {
\r
8548 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8554 CloseHandle(cp->hProcess);
\r
8558 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8562 closesocket(cp->sock);
\r
8567 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8568 closesocket(cp->sock);
\r
8569 closesocket(cp->sock2);
\r
8577 InterruptChildProcess(ProcRef pr)
\r
8581 cp = (ChildProc *) pr;
\r
8582 if (cp == NULL) return;
\r
8583 switch (cp->kind) {
\r
8585 /* The following doesn't work because the chess program
\r
8586 doesn't "have the same console" as WinBoard. Maybe
\r
8587 we could arrange for this even though neither WinBoard
\r
8588 nor the chess program uses a console for stdio */
\r
8589 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8594 /* Can't interrupt */
\r
8598 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8605 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8607 char cmdLine[MSG_SIZ];
\r
8609 if (port[0] == NULLCHAR) {
\r
8610 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8612 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8614 return StartChildProcess(cmdLine, "", pr);
\r
8618 /* Code to open TCP sockets */
\r
8621 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8626 struct sockaddr_in sa, mysa;
\r
8627 struct hostent FAR *hp;
\r
8628 unsigned short uport;
\r
8629 WORD wVersionRequested;
\r
8632 /* Initialize socket DLL */
\r
8633 wVersionRequested = MAKEWORD(1, 1);
\r
8634 err = WSAStartup(wVersionRequested, &wsaData);
\r
8635 if (err != 0) return err;
\r
8638 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8639 err = WSAGetLastError();
\r
8644 /* Bind local address using (mostly) don't-care values.
\r
8646 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8647 mysa.sin_family = AF_INET;
\r
8648 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8649 uport = (unsigned short) 0;
\r
8650 mysa.sin_port = htons(uport);
\r
8651 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8652 == SOCKET_ERROR) {
\r
8653 err = WSAGetLastError();
\r
8658 /* Resolve remote host name */
\r
8659 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8660 if (!(hp = gethostbyname(host))) {
\r
8661 unsigned int b0, b1, b2, b3;
\r
8663 err = WSAGetLastError();
\r
8665 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8666 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8667 hp->h_addrtype = AF_INET;
\r
8669 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8670 hp->h_addr_list[0] = (char *) malloc(4);
\r
8671 hp->h_addr_list[0][0] = (char) b0;
\r
8672 hp->h_addr_list[0][1] = (char) b1;
\r
8673 hp->h_addr_list[0][2] = (char) b2;
\r
8674 hp->h_addr_list[0][3] = (char) b3;
\r
8680 sa.sin_family = hp->h_addrtype;
\r
8681 uport = (unsigned short) atoi(port);
\r
8682 sa.sin_port = htons(uport);
\r
8683 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8685 /* Make connection */
\r
8686 if (connect(s, (struct sockaddr *) &sa,
\r
8687 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8688 err = WSAGetLastError();
\r
8693 /* Prepare return value */
\r
8694 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8695 cp->kind = CPSock;
\r
8697 *pr = (ProcRef *) cp;
\r
8703 OpenCommPort(char *name, ProcRef *pr)
\r
8708 char fullname[MSG_SIZ];
\r
8710 if (*name != '\\')
\r
8711 sprintf(fullname, "\\\\.\\%s", name);
\r
8713 strcpy(fullname, name);
\r
8715 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8716 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8717 if (h == (HANDLE) -1) {
\r
8718 return GetLastError();
\r
8722 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8724 /* Accumulate characters until a 100ms pause, then parse */
\r
8725 ct.ReadIntervalTimeout = 100;
\r
8726 ct.ReadTotalTimeoutMultiplier = 0;
\r
8727 ct.ReadTotalTimeoutConstant = 0;
\r
8728 ct.WriteTotalTimeoutMultiplier = 0;
\r
8729 ct.WriteTotalTimeoutConstant = 0;
\r
8730 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8732 /* Prepare return value */
\r
8733 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8734 cp->kind = CPComm;
\r
8737 *pr = (ProcRef *) cp;
\r
8743 OpenLoopback(ProcRef *pr)
\r
8745 DisplayFatalError("Not implemented", 0, 1);
\r
8751 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8756 struct sockaddr_in sa, mysa;
\r
8757 struct hostent FAR *hp;
\r
8758 unsigned short uport;
\r
8759 WORD wVersionRequested;
\r
8762 char stderrPortStr[MSG_SIZ];
\r
8764 /* Initialize socket DLL */
\r
8765 wVersionRequested = MAKEWORD(1, 1);
\r
8766 err = WSAStartup(wVersionRequested, &wsaData);
\r
8767 if (err != 0) return err;
\r
8769 /* Resolve remote host name */
\r
8770 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8771 if (!(hp = gethostbyname(host))) {
\r
8772 unsigned int b0, b1, b2, b3;
\r
8774 err = WSAGetLastError();
\r
8776 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8777 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8778 hp->h_addrtype = AF_INET;
\r
8780 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8781 hp->h_addr_list[0] = (char *) malloc(4);
\r
8782 hp->h_addr_list[0][0] = (char) b0;
\r
8783 hp->h_addr_list[0][1] = (char) b1;
\r
8784 hp->h_addr_list[0][2] = (char) b2;
\r
8785 hp->h_addr_list[0][3] = (char) b3;
\r
8791 sa.sin_family = hp->h_addrtype;
\r
8792 uport = (unsigned short) 514;
\r
8793 sa.sin_port = htons(uport);
\r
8794 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8796 /* Bind local socket to unused "privileged" port address
\r
8798 s = INVALID_SOCKET;
\r
8799 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8800 mysa.sin_family = AF_INET;
\r
8801 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8802 for (fromPort = 1023;; fromPort--) {
\r
8803 if (fromPort < 0) {
\r
8805 return WSAEADDRINUSE;
\r
8807 if (s == INVALID_SOCKET) {
\r
8808 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8809 err = WSAGetLastError();
\r
8814 uport = (unsigned short) fromPort;
\r
8815 mysa.sin_port = htons(uport);
\r
8816 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8817 == SOCKET_ERROR) {
\r
8818 err = WSAGetLastError();
\r
8819 if (err == WSAEADDRINUSE) continue;
\r
8823 if (connect(s, (struct sockaddr *) &sa,
\r
8824 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8825 err = WSAGetLastError();
\r
8826 if (err == WSAEADDRINUSE) {
\r
8837 /* Bind stderr local socket to unused "privileged" port address
\r
8839 s2 = INVALID_SOCKET;
\r
8840 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8841 mysa.sin_family = AF_INET;
\r
8842 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8843 for (fromPort = 1023;; fromPort--) {
\r
8844 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8845 if (fromPort < 0) {
\r
8846 (void) closesocket(s);
\r
8848 return WSAEADDRINUSE;
\r
8850 if (s2 == INVALID_SOCKET) {
\r
8851 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8852 err = WSAGetLastError();
\r
8858 uport = (unsigned short) fromPort;
\r
8859 mysa.sin_port = htons(uport);
\r
8860 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8861 == SOCKET_ERROR) {
\r
8862 err = WSAGetLastError();
\r
8863 if (err == WSAEADDRINUSE) continue;
\r
8864 (void) closesocket(s);
\r
8868 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8869 err = WSAGetLastError();
\r
8870 if (err == WSAEADDRINUSE) {
\r
8872 s2 = INVALID_SOCKET;
\r
8875 (void) closesocket(s);
\r
8876 (void) closesocket(s2);
\r
8882 prevStderrPort = fromPort; // remember port used
\r
8883 sprintf(stderrPortStr, "%d", fromPort);
\r
8885 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8886 err = WSAGetLastError();
\r
8887 (void) closesocket(s);
\r
8888 (void) closesocket(s2);
\r
8893 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8894 err = WSAGetLastError();
\r
8895 (void) closesocket(s);
\r
8896 (void) closesocket(s2);
\r
8900 if (*user == NULLCHAR) user = UserName();
\r
8901 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8902 err = WSAGetLastError();
\r
8903 (void) closesocket(s);
\r
8904 (void) closesocket(s2);
\r
8908 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8909 err = WSAGetLastError();
\r
8910 (void) closesocket(s);
\r
8911 (void) closesocket(s2);
\r
8916 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8917 err = WSAGetLastError();
\r
8918 (void) closesocket(s);
\r
8919 (void) closesocket(s2);
\r
8923 (void) closesocket(s2); /* Stop listening */
\r
8925 /* Prepare return value */
\r
8926 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8927 cp->kind = CPRcmd;
\r
8930 *pr = (ProcRef *) cp;
\r
8937 AddInputSource(ProcRef pr, int lineByLine,
\r
8938 InputCallback func, VOIDSTAR closure)
\r
8940 InputSource *is, *is2 = NULL;
\r
8941 ChildProc *cp = (ChildProc *) pr;
\r
8943 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8944 is->lineByLine = lineByLine;
\r
8946 is->closure = closure;
\r
8947 is->second = NULL;
\r
8948 is->next = is->buf;
\r
8949 if (pr == NoProc) {
\r
8950 is->kind = CPReal;
\r
8951 consoleInputSource = is;
\r
8953 is->kind = cp->kind;
\r
8955 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8956 we create all threads suspended so that the is->hThread variable can be
\r
8957 safely assigned, then let the threads start with ResumeThread.
\r
8959 switch (cp->kind) {
\r
8961 is->hFile = cp->hFrom;
\r
8962 cp->hFrom = NULL; /* now owned by InputThread */
\r
8964 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8965 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8969 is->hFile = cp->hFrom;
\r
8970 cp->hFrom = NULL; /* now owned by InputThread */
\r
8972 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
8973 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8977 is->sock = cp->sock;
\r
8979 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8980 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8984 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
8986 is->sock = cp->sock;
\r
8988 is2->sock = cp->sock2;
\r
8989 is2->second = is2;
\r
8991 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8992 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8994 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8995 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
8999 if( is->hThread != NULL ) {
\r
9000 ResumeThread( is->hThread );
\r
9003 if( is2 != NULL && is2->hThread != NULL ) {
\r
9004 ResumeThread( is2->hThread );
\r
9008 return (InputSourceRef) is;
\r
9012 RemoveInputSource(InputSourceRef isr)
\r
9016 is = (InputSource *) isr;
\r
9017 is->hThread = NULL; /* tell thread to stop */
\r
9018 CloseHandle(is->hThread);
\r
9019 if (is->second != NULL) {
\r
9020 is->second->hThread = NULL;
\r
9021 CloseHandle(is->second->hThread);
\r
9025 int no_wrap(char *message, int count)
\r
9027 ConsoleOutput(message, count, FALSE);
\r
9032 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9035 int outCount = SOCKET_ERROR;
\r
9036 ChildProc *cp = (ChildProc *) pr;
\r
9037 static OVERLAPPED ovl;
\r
9038 static int line = 0;
\r
9042 if (appData.noJoin || !appData.useInternalWrap)
\r
9043 return no_wrap(message, count);
\r
9046 int width = get_term_width();
\r
9047 int len = wrap(NULL, message, count, width, &line);
\r
9048 char *msg = malloc(len);
\r
9052 return no_wrap(message, count);
\r
9055 dbgchk = wrap(msg, message, count, width, &line);
\r
9056 if (dbgchk != len && appData.debugMode)
\r
9057 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9058 ConsoleOutput(msg, len, FALSE);
\r
9065 if (ovl.hEvent == NULL) {
\r
9066 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9068 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9070 switch (cp->kind) {
\r
9073 outCount = send(cp->sock, message, count, 0);
\r
9074 if (outCount == SOCKET_ERROR) {
\r
9075 *outError = WSAGetLastError();
\r
9077 *outError = NO_ERROR;
\r
9082 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9083 &dOutCount, NULL)) {
\r
9084 *outError = NO_ERROR;
\r
9085 outCount = (int) dOutCount;
\r
9087 *outError = GetLastError();
\r
9092 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9093 &dOutCount, &ovl);
\r
9094 if (*outError == NO_ERROR) {
\r
9095 outCount = (int) dOutCount;
\r
9103 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9106 /* Ignore delay, not implemented for WinBoard */
\r
9107 return OutputToProcess(pr, message, count, outError);
\r
9112 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9113 char *buf, int count, int error)
\r
9115 DisplayFatalError("Not implemented", 0, 1);
\r
9118 /* see wgamelist.c for Game List functions */
\r
9119 /* see wedittags.c for Edit Tags functions */
\r
9126 char buf[MSG_SIZ];
\r
9129 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9130 f = fopen(buf, "r");
\r
9132 ProcessICSInitScript(f);
\r
9140 StartAnalysisClock()
\r
9142 if (analysisTimerEvent) return;
\r
9143 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9144 (UINT) 2000, NULL);
\r
9148 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9150 highlightInfo.sq[0].x = fromX;
\r
9151 highlightInfo.sq[0].y = fromY;
\r
9152 highlightInfo.sq[1].x = toX;
\r
9153 highlightInfo.sq[1].y = toY;
\r
9159 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9160 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9164 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9166 premoveHighlightInfo.sq[0].x = fromX;
\r
9167 premoveHighlightInfo.sq[0].y = fromY;
\r
9168 premoveHighlightInfo.sq[1].x = toX;
\r
9169 premoveHighlightInfo.sq[1].y = toY;
\r
9173 ClearPremoveHighlights()
\r
9175 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9176 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9180 ShutDownFrontEnd()
\r
9182 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9183 DeleteClipboardTempFiles();
\r
9189 if (IsIconic(hwndMain))
\r
9190 ShowWindow(hwndMain, SW_RESTORE);
\r
9192 SetActiveWindow(hwndMain);
\r
9196 * Prototypes for animation support routines
\r
9198 static void ScreenSquare(int column, int row, POINT * pt);
\r
9199 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9200 POINT frames[], int * nFrames);
\r
9204 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9205 { // [HGM] atomic: animate blast wave
\r
9207 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9208 explodeInfo.fromX = fromX;
\r
9209 explodeInfo.fromY = fromY;
\r
9210 explodeInfo.toX = toX;
\r
9211 explodeInfo.toY = toY;
\r
9212 for(i=1; i<nFrames; i++) {
\r
9213 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9214 DrawPosition(FALSE, NULL);
\r
9215 Sleep(appData.animSpeed);
\r
9217 explodeInfo.radius = 0;
\r
9218 DrawPosition(TRUE, NULL);
\r
9224 AnimateMove(board, fromX, fromY, toX, toY)
\r
9231 ChessSquare piece;
\r
9232 POINT start, finish, mid;
\r
9233 POINT frames[kFactor * 2 + 1];
\r
9236 if (!appData.animate) return;
\r
9237 if (doingSizing) return;
\r
9238 if (fromY < 0 || fromX < 0) return;
\r
9239 piece = board[fromY][fromX];
\r
9240 if (piece >= EmptySquare) return;
\r
9242 ScreenSquare(fromX, fromY, &start);
\r
9243 ScreenSquare(toX, toY, &finish);
\r
9245 /* All pieces except knights move in straight line */
\r
9246 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9247 mid.x = start.x + (finish.x - start.x) / 2;
\r
9248 mid.y = start.y + (finish.y - start.y) / 2;
\r
9250 /* Knight: make diagonal movement then straight */
\r
9251 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9252 mid.x = start.x + (finish.x - start.x) / 2;
\r
9256 mid.y = start.y + (finish.y - start.y) / 2;
\r
9260 /* Don't use as many frames for very short moves */
\r
9261 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9262 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9264 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9266 animInfo.from.x = fromX;
\r
9267 animInfo.from.y = fromY;
\r
9268 animInfo.to.x = toX;
\r
9269 animInfo.to.y = toY;
\r
9270 animInfo.lastpos = start;
\r
9271 animInfo.piece = piece;
\r
9272 for (n = 0; n < nFrames; n++) {
\r
9273 animInfo.pos = frames[n];
\r
9274 DrawPosition(FALSE, NULL);
\r
9275 animInfo.lastpos = animInfo.pos;
\r
9276 Sleep(appData.animSpeed);
\r
9278 animInfo.pos = finish;
\r
9279 DrawPosition(FALSE, NULL);
\r
9280 animInfo.piece = EmptySquare;
\r
9281 if(gameInfo.variant == VariantAtomic &&
\r
9282 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9283 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9286 /* Convert board position to corner of screen rect and color */
\r
9289 ScreenSquare(column, row, pt)
\r
9290 int column; int row; POINT * pt;
\r
9293 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9294 pt->y = lineGap + row * (squareSize + lineGap);
\r
9296 pt->x = lineGap + column * (squareSize + lineGap);
\r
9297 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9301 /* Generate a series of frame coords from start->mid->finish.
\r
9302 The movement rate doubles until the half way point is
\r
9303 reached, then halves back down to the final destination,
\r
9304 which gives a nice slow in/out effect. The algorithmn
\r
9305 may seem to generate too many intermediates for short
\r
9306 moves, but remember that the purpose is to attract the
\r
9307 viewers attention to the piece about to be moved and
\r
9308 then to where it ends up. Too few frames would be less
\r
9312 Tween(start, mid, finish, factor, frames, nFrames)
\r
9313 POINT * start; POINT * mid;
\r
9314 POINT * finish; int factor;
\r
9315 POINT frames[]; int * nFrames;
\r
9317 int n, fraction = 1, count = 0;
\r
9319 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9320 for (n = 0; n < factor; n++)
\r
9322 for (n = 0; n < factor; n++) {
\r
9323 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9324 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9326 fraction = fraction / 2;
\r
9330 frames[count] = *mid;
\r
9333 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9335 for (n = 0; n < factor; n++) {
\r
9336 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9337 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9339 fraction = fraction * 2;
\r
9345 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9347 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9349 EvalGraphSet( first, last, current, pvInfoList );
\r