2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P(());
\r
113 ChessSquare piece;
\r
114 POINT pos; /* window coordinates of current pos */
\r
115 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
116 POINT from; /* board coordinates of the piece's orig pos */
\r
117 POINT to; /* board coordinates of the piece's new pos */
\r
120 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
123 POINT start; /* window coordinates of start pos */
\r
124 POINT pos; /* window coordinates of current pos */
\r
125 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
126 POINT from; /* board coordinates of the piece's orig pos */
\r
129 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
132 POINT sq[2]; /* board coordinates of from, to squares */
\r
135 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
136 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
138 typedef struct { // [HGM] atomic
\r
139 int fromX, fromY, toX, toY, radius;
\r
142 static ExplodeInfo explodeInfo;
\r
144 /* Window class names */
\r
145 char szAppName[] = "WinBoard";
\r
146 char szConsoleName[] = "WBConsole";
\r
148 /* Title bar text */
\r
149 char szTitle[] = "WinBoard";
\r
150 char szConsoleTitle[] = "I C S Interaction";
\r
153 char *settingsFileName;
\r
154 Boolean saveSettingsOnExit;
\r
155 char installDir[MSG_SIZ];
\r
156 int errorExitStatus;
\r
158 BoardSize boardSize;
\r
159 Boolean chessProgram;
\r
160 //static int boardX, boardY;
\r
161 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
\r
162 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 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3131 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3132 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3133 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3134 SelectObject(hdcSeek, oldBrush);
\r
3138 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3140 static Board lastReq, lastDrawn;
\r
3141 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3142 static int lastDrawnFlipView = 0;
\r
3143 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3144 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3147 HBITMAP bufferBitmap;
\r
3148 HBITMAP oldBitmap;
\r
3150 HRGN clips[MAX_CLIPS];
\r
3151 ChessSquare dragged_piece = EmptySquare;
\r
3153 /* I'm undecided on this - this function figures out whether a full
\r
3154 * repaint is necessary on its own, so there's no real reason to have the
\r
3155 * caller tell it that. I think this can safely be set to FALSE - but
\r
3156 * if we trust the callers not to request full repaints unnessesarily, then
\r
3157 * we could skip some clipping work. In other words, only request a full
\r
3158 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3159 * gamestart and similar) --Hawk
\r
3161 Boolean fullrepaint = repaint;
\r
3163 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3165 if( DrawPositionNeedsFullRepaint() ) {
\r
3166 fullrepaint = TRUE;
\r
3169 if (board == NULL) {
\r
3170 if (!lastReqValid) {
\r
3175 CopyBoard(lastReq, board);
\r
3179 if (doingSizing) {
\r
3183 if (IsIconic(hwndMain)) {
\r
3187 if (hdc == NULL) {
\r
3188 hdc = GetDC(hwndMain);
\r
3189 if (!appData.monoMode) {
\r
3190 SelectPalette(hdc, hPal, FALSE);
\r
3191 RealizePalette(hdc);
\r
3195 releaseDC = FALSE;
\r
3198 /* Create some work-DCs */
\r
3199 hdcmem = CreateCompatibleDC(hdc);
\r
3200 tmphdc = CreateCompatibleDC(hdc);
\r
3202 /* If dragging is in progress, we temporarely remove the piece */
\r
3203 /* [HGM] or temporarily decrease count if stacked */
\r
3204 /* !! Moved to before board compare !! */
\r
3205 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3206 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3207 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3208 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3209 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3211 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3212 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3213 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3215 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3218 /* Figure out which squares need updating by comparing the
\r
3219 * newest board with the last drawn board and checking if
\r
3220 * flipping has changed.
\r
3222 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3223 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3224 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3225 if (lastDrawn[row][column] != board[row][column]) {
\r
3226 SquareToPos(row, column, &x, &y);
\r
3227 clips[num_clips++] =
\r
3228 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3232 for (i=0; i<2; i++) {
\r
3233 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3234 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3235 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3236 lastDrawnHighlight.sq[i].y >= 0) {
\r
3237 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3238 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3239 clips[num_clips++] =
\r
3240 CreateRectRgn(x - lineGap, y - lineGap,
\r
3241 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3243 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3244 SquareToPos(highlightInfo.sq[i].y, highlightInfo.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
3251 for (i=0; i<2; i++) {
\r
3252 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3253 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3254 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3255 lastDrawnPremove.sq[i].y >= 0) {
\r
3256 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3257 lastDrawnPremove.sq[i].x, &x, &y);
\r
3258 clips[num_clips++] =
\r
3259 CreateRectRgn(x - lineGap, y - lineGap,
\r
3260 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3262 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3263 premoveHighlightInfo.sq[i].y >= 0) {
\r
3264 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3265 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3266 clips[num_clips++] =
\r
3267 CreateRectRgn(x - lineGap, y - lineGap,
\r
3268 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3273 fullrepaint = TRUE;
\r
3276 /* Create a buffer bitmap - this is the actual bitmap
\r
3277 * being written to. When all the work is done, we can
\r
3278 * copy it to the real DC (the screen). This avoids
\r
3279 * the problems with flickering.
\r
3281 GetClientRect(hwndMain, &Rect);
\r
3282 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3283 Rect.bottom-Rect.top+1);
\r
3284 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3285 if (!appData.monoMode) {
\r
3286 SelectPalette(hdcmem, hPal, FALSE);
\r
3289 /* Create clips for dragging */
\r
3290 if (!fullrepaint) {
\r
3291 if (dragInfo.from.x >= 0) {
\r
3292 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3293 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3295 if (dragInfo.start.x >= 0) {
\r
3296 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3297 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3299 if (dragInfo.pos.x >= 0) {
\r
3300 x = dragInfo.pos.x - squareSize / 2;
\r
3301 y = dragInfo.pos.y - squareSize / 2;
\r
3302 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3304 if (dragInfo.lastpos.x >= 0) {
\r
3305 x = dragInfo.lastpos.x - squareSize / 2;
\r
3306 y = dragInfo.lastpos.y - squareSize / 2;
\r
3307 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3311 /* Are we animating a move?
\r
3313 * - remove the piece from the board (temporarely)
\r
3314 * - calculate the clipping region
\r
3316 if (!fullrepaint) {
\r
3317 if (animInfo.piece != EmptySquare) {
\r
3318 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3319 x = boardRect.left + animInfo.lastpos.x;
\r
3320 y = boardRect.top + animInfo.lastpos.y;
\r
3321 x2 = boardRect.left + animInfo.pos.x;
\r
3322 y2 = boardRect.top + animInfo.pos.y;
\r
3323 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3324 /* Slight kludge. The real problem is that after AnimateMove is
\r
3325 done, the position on the screen does not match lastDrawn.
\r
3326 This currently causes trouble only on e.p. captures in
\r
3327 atomic, where the piece moves to an empty square and then
\r
3328 explodes. The old and new positions both had an empty square
\r
3329 at the destination, but animation has drawn a piece there and
\r
3330 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3331 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3335 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3336 if (num_clips == 0)
\r
3337 fullrepaint = TRUE;
\r
3339 /* Set clipping on the memory DC */
\r
3340 if (!fullrepaint) {
\r
3341 SelectClipRgn(hdcmem, clips[0]);
\r
3342 for (x = 1; x < num_clips; x++) {
\r
3343 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3344 abort(); // this should never ever happen!
\r
3348 /* Do all the drawing to the memory DC */
\r
3349 if(explodeInfo.radius) { // [HGM] atomic
\r
3351 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3352 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3353 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3354 x += squareSize/2;
\r
3355 y += squareSize/2;
\r
3356 if(!fullrepaint) {
\r
3357 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3358 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3360 DrawGridOnDC(hdcmem);
\r
3361 DrawHighlightsOnDC(hdcmem);
\r
3362 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3363 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3364 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3365 SelectObject(hdcmem, oldBrush);
\r
3367 DrawGridOnDC(hdcmem);
\r
3368 DrawHighlightsOnDC(hdcmem);
\r
3369 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3371 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3372 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3373 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3374 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3375 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3376 SquareToPos(row, column, &x, &y);
\r
3377 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3378 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3379 SelectObject(hdcmem, oldBrush);
\r
3384 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3385 if(appData.autoLogo) {
\r
3387 switch(gameMode) { // pick logos based on game mode
\r
3388 case IcsObserving:
\r
3389 whiteLogo = second.programLogo; // ICS logo
\r
3390 blackLogo = second.programLogo;
\r
3393 case IcsPlayingWhite:
\r
3394 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3395 blackLogo = second.programLogo; // ICS logo
\r
3397 case IcsPlayingBlack:
\r
3398 whiteLogo = second.programLogo; // ICS logo
\r
3399 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3401 case TwoMachinesPlay:
\r
3402 if(first.twoMachinesColor[0] == 'b') {
\r
3403 whiteLogo = second.programLogo;
\r
3404 blackLogo = first.programLogo;
\r
3407 case MachinePlaysWhite:
\r
3408 blackLogo = userLogo;
\r
3410 case MachinePlaysBlack:
\r
3411 whiteLogo = userLogo;
\r
3412 blackLogo = first.programLogo;
\r
3415 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3416 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3419 if( appData.highlightMoveWithArrow ) {
\r
3420 DrawArrowHighlight(hdcmem);
\r
3423 DrawCoordsOnDC(hdcmem);
\r
3425 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3426 /* to make sure lastDrawn contains what is actually drawn */
\r
3428 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3429 if (dragged_piece != EmptySquare) {
\r
3430 /* [HGM] or restack */
\r
3431 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3432 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3434 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3435 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3436 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3437 x = dragInfo.pos.x - squareSize / 2;
\r
3438 y = dragInfo.pos.y - squareSize / 2;
\r
3439 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3440 ((int) dragged_piece < (int) BlackPawn),
\r
3441 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3444 /* Put the animated piece back into place and draw it */
\r
3445 if (animInfo.piece != EmptySquare) {
\r
3446 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3447 x = boardRect.left + animInfo.pos.x;
\r
3448 y = boardRect.top + animInfo.pos.y;
\r
3449 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3450 ((int) animInfo.piece < (int) BlackPawn),
\r
3451 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3454 /* Release the bufferBitmap by selecting in the old bitmap
\r
3455 * and delete the memory DC
\r
3457 SelectObject(hdcmem, oldBitmap);
\r
3460 /* Set clipping on the target DC */
\r
3461 if (!fullrepaint) {
\r
3462 SelectClipRgn(hdc, clips[0]);
\r
3463 for (x = 1; x < num_clips; x++) {
\r
3464 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3465 abort(); // this should never ever happen!
\r
3469 /* Copy the new bitmap onto the screen in one go.
\r
3470 * This way we avoid any flickering
\r
3472 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3473 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3474 boardRect.right - boardRect.left,
\r
3475 boardRect.bottom - boardRect.top,
\r
3476 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3477 if(saveDiagFlag) {
\r
3478 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3479 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3481 GetObject(bufferBitmap, sizeof(b), &b);
\r
3482 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3483 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3484 bih.biWidth = b.bmWidth;
\r
3485 bih.biHeight = b.bmHeight;
\r
3487 bih.biBitCount = b.bmBitsPixel;
\r
3488 bih.biCompression = 0;
\r
3489 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3490 bih.biXPelsPerMeter = 0;
\r
3491 bih.biYPelsPerMeter = 0;
\r
3492 bih.biClrUsed = 0;
\r
3493 bih.biClrImportant = 0;
\r
3494 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3495 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3496 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3497 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3499 wb = b.bmWidthBytes;
\r
3501 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3502 int k = ((int*) pData)[i];
\r
3503 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3504 if(j >= 16) break;
\r
3506 if(j >= nrColors) nrColors = j+1;
\r
3508 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3510 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3511 for(w=0; w<(wb>>2); w+=2) {
\r
3512 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3513 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3514 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3515 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3516 pData[p++] = m | j<<4;
\r
3518 while(p&3) pData[p++] = 0;
\r
3521 wb = ((wb+31)>>5)<<2;
\r
3523 // write BITMAPFILEHEADER
\r
3524 fprintf(diagFile, "BM");
\r
3525 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3526 fputDW(diagFile, 0);
\r
3527 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3528 // write BITMAPINFOHEADER
\r
3529 fputDW(diagFile, 40);
\r
3530 fputDW(diagFile, b.bmWidth);
\r
3531 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3532 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3533 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3534 fputDW(diagFile, 0);
\r
3535 fputDW(diagFile, 0);
\r
3536 fputDW(diagFile, 0);
\r
3537 fputDW(diagFile, 0);
\r
3538 fputDW(diagFile, 0);
\r
3539 fputDW(diagFile, 0);
\r
3540 // write color table
\r
3542 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3543 // write bitmap data
\r
3544 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3545 fputc(pData[i], diagFile);
\r
3549 SelectObject(tmphdc, oldBitmap);
\r
3551 /* Massive cleanup */
\r
3552 for (x = 0; x < num_clips; x++)
\r
3553 DeleteObject(clips[x]);
\r
3556 DeleteObject(bufferBitmap);
\r
3559 ReleaseDC(hwndMain, hdc);
\r
3561 if (lastDrawnFlipView != flipView) {
\r
3563 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3565 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3568 /* CopyBoard(lastDrawn, board);*/
\r
3569 lastDrawnHighlight = highlightInfo;
\r
3570 lastDrawnPremove = premoveHighlightInfo;
\r
3571 lastDrawnFlipView = flipView;
\r
3572 lastDrawnValid = 1;
\r
3575 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3580 saveDiagFlag = 1; diagFile = f;
\r
3581 HDCDrawPosition(NULL, TRUE, NULL);
\r
3585 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3592 /*---------------------------------------------------------------------------*\
\r
3593 | CLIENT PAINT PROCEDURE
\r
3594 | This is the main event-handler for the WM_PAINT message.
\r
3596 \*---------------------------------------------------------------------------*/
\r
3598 PaintProc(HWND hwnd)
\r
3604 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3605 if (IsIconic(hwnd)) {
\r
3606 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3608 if (!appData.monoMode) {
\r
3609 SelectPalette(hdc, hPal, FALSE);
\r
3610 RealizePalette(hdc);
\r
3612 HDCDrawPosition(hdc, 1, NULL);
\r
3614 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3615 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3616 ETO_CLIPPED|ETO_OPAQUE,
\r
3617 &messageRect, messageText, strlen(messageText), NULL);
\r
3618 SelectObject(hdc, oldFont);
\r
3619 DisplayBothClocks();
\r
3621 EndPaint(hwnd,&ps);
\r
3629 * If the user selects on a border boundary, return -1; if off the board,
\r
3630 * return -2. Otherwise map the event coordinate to the square.
\r
3631 * The offset boardRect.left or boardRect.top must already have been
\r
3632 * subtracted from x.
\r
3634 int EventToSquare(x, limit)
\r
3642 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3644 x /= (squareSize + lineGap);
\r
3656 DropEnable dropEnables[] = {
\r
3657 { 'P', DP_Pawn, "Pawn" },
\r
3658 { 'N', DP_Knight, "Knight" },
\r
3659 { 'B', DP_Bishop, "Bishop" },
\r
3660 { 'R', DP_Rook, "Rook" },
\r
3661 { 'Q', DP_Queen, "Queen" },
\r
3665 SetupDropMenu(HMENU hmenu)
\r
3667 int i, count, enable;
\r
3669 extern char white_holding[], black_holding[];
\r
3670 char item[MSG_SIZ];
\r
3672 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3673 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3674 dropEnables[i].piece);
\r
3676 while (p && *p++ == dropEnables[i].piece) count++;
\r
3677 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3678 enable = count > 0 || !appData.testLegality
\r
3679 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3680 && !appData.icsActive);
\r
3681 ModifyMenu(hmenu, dropEnables[i].command,
\r
3682 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3683 dropEnables[i].command, item);
\r
3687 void DragPieceBegin(int x, int y)
\r
3689 dragInfo.lastpos.x = boardRect.left + x;
\r
3690 dragInfo.lastpos.y = boardRect.top + y;
\r
3691 dragInfo.from.x = fromX;
\r
3692 dragInfo.from.y = fromY;
\r
3693 dragInfo.start = dragInfo.from;
\r
3694 SetCapture(hwndMain);
\r
3697 void DragPieceEnd(int x, int y)
\r
3700 dragInfo.start.x = dragInfo.start.y = -1;
\r
3701 dragInfo.from = dragInfo.start;
\r
3702 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3705 /* Event handler for mouse messages */
\r
3707 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3711 static int recursive = 0;
\r
3713 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3716 if (message == WM_MBUTTONUP) {
\r
3717 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3718 to the middle button: we simulate pressing the left button too!
\r
3720 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3721 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3727 pt.x = LOWORD(lParam);
\r
3728 pt.y = HIWORD(lParam);
\r
3729 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3730 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3731 if (!flipView && y >= 0) {
\r
3732 y = BOARD_HEIGHT - 1 - y;
\r
3734 if (flipView && x >= 0) {
\r
3735 x = BOARD_WIDTH - 1 - x;
\r
3738 switch (message) {
\r
3739 case WM_LBUTTONDOWN:
\r
3740 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3741 if (gameMode == EditPosition) {
\r
3742 SetWhiteToPlayEvent();
\r
3743 } else if (gameMode == IcsPlayingBlack ||
\r
3744 gameMode == MachinePlaysWhite) {
\r
3746 } else if (gameMode == EditGame) {
\r
3747 AdjustClock(flipClock, -1);
\r
3749 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3750 if (gameMode == EditPosition) {
\r
3751 SetBlackToPlayEvent();
\r
3752 } else if (gameMode == IcsPlayingWhite ||
\r
3753 gameMode == MachinePlaysBlack) {
\r
3755 } else if (gameMode == EditGame) {
\r
3756 AdjustClock(!flipClock, -1);
\r
3759 dragInfo.start.x = dragInfo.start.y = -1;
\r
3760 dragInfo.from = dragInfo.start;
\r
3761 if(fromX == -1 && frozen) { // not sure where this is for
\r
3762 fromX = fromY = -1;
\r
3763 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3766 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3767 DrawPosition(TRUE, NULL);
\r
3770 case WM_LBUTTONUP:
\r
3771 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3772 DrawPosition(TRUE, NULL);
\r
3775 case WM_MOUSEMOVE:
\r
3776 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3777 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3778 if ((appData.animateDragging || appData.highlightDragging)
\r
3779 && (wParam & MK_LBUTTON)
\r
3780 && dragInfo.from.x >= 0)
\r
3782 BOOL full_repaint = FALSE;
\r
3784 if (appData.animateDragging) {
\r
3785 dragInfo.pos = pt;
\r
3787 if (appData.highlightDragging) {
\r
3788 SetHighlights(fromX, fromY, x, y);
\r
3789 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3790 full_repaint = TRUE;
\r
3794 DrawPosition( full_repaint, NULL);
\r
3796 dragInfo.lastpos = dragInfo.pos;
\r
3800 case WM_MOUSEWHEEL: // [DM]
\r
3801 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3802 /* Mouse Wheel is being rolled forward
\r
3803 * Play moves forward
\r
3805 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3806 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3807 /* Mouse Wheel is being rolled backward
\r
3808 * Play moves backward
\r
3810 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3811 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3815 case WM_MBUTTONUP:
\r
3816 case WM_RBUTTONUP:
\r
3818 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3821 case WM_MBUTTONDOWN:
\r
3822 case WM_RBUTTONDOWN:
\r
3825 fromX = fromY = -1;
\r
3826 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3827 dragInfo.start.x = dragInfo.start.y = -1;
\r
3828 dragInfo.from = dragInfo.start;
\r
3829 dragInfo.lastpos = dragInfo.pos;
\r
3830 if (appData.highlightDragging) {
\r
3831 ClearHighlights();
\r
3834 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3835 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3836 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3837 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3838 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3841 DrawPosition(TRUE, NULL);
\r
3843 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3846 if (message == WM_MBUTTONDOWN) {
\r
3847 buttonCount = 3; /* even if system didn't think so */
\r
3848 if (wParam & MK_SHIFT)
\r
3849 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3851 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3852 } else { /* message == WM_RBUTTONDOWN */
\r
3853 /* Just have one menu, on the right button. Windows users don't
\r
3854 think to try the middle one, and sometimes other software steals
\r
3855 it, or it doesn't really exist. */
\r
3856 if(gameInfo.variant != VariantShogi)
\r
3857 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3859 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3863 SetCapture(hwndMain);
3866 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3867 SetupDropMenu(hmenu);
\r
3868 MenuPopup(hwnd, pt, hmenu, -1);
\r
3878 /* Preprocess messages for buttons in main window */
\r
3880 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3882 int id = GetWindowLong(hwnd, GWL_ID);
\r
3885 for (i=0; i<N_BUTTONS; i++) {
\r
3886 if (buttonDesc[i].id == id) break;
\r
3888 if (i == N_BUTTONS) return 0;
\r
3889 switch (message) {
\r
3894 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3895 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3902 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3905 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3906 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3907 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3908 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3910 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3912 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3913 PopUpMoveDialog((char)wParam);
\r
3919 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3922 /* Process messages for Promotion dialog box */
\r
3924 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3928 switch (message) {
\r
3929 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3930 /* Center the dialog over the application window */
\r
3931 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3932 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3933 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3934 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3935 SW_SHOW : SW_HIDE);
\r
3936 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3937 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3938 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3939 PieceToChar(WhiteAngel) != '~') ||
\r
3940 (PieceToChar(BlackAngel) >= 'A' &&
\r
3941 PieceToChar(BlackAngel) != '~') ) ?
\r
3942 SW_SHOW : SW_HIDE);
\r
3943 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3944 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3945 PieceToChar(WhiteMarshall) != '~') ||
\r
3946 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3947 PieceToChar(BlackMarshall) != '~') ) ?
\r
3948 SW_SHOW : SW_HIDE);
\r
3949 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3950 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3951 gameInfo.variant != VariantShogi ?
\r
3952 SW_SHOW : SW_HIDE);
\r
3953 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3954 gameInfo.variant != VariantShogi ?
\r
3955 SW_SHOW : SW_HIDE);
\r
3956 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3957 gameInfo.variant == VariantShogi ?
\r
3958 SW_SHOW : SW_HIDE);
\r
3959 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3960 gameInfo.variant == VariantShogi ?
\r
3961 SW_SHOW : SW_HIDE);
\r
3962 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3963 gameInfo.variant == VariantSuper ?
\r
3964 SW_SHOW : SW_HIDE);
\r
3967 case WM_COMMAND: /* message: received a command */
\r
3968 switch (LOWORD(wParam)) {
\r
3970 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3971 ClearHighlights();
\r
3972 DrawPosition(FALSE, NULL);
\r
3975 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3978 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3981 promoChar = PieceToChar(BlackRook);
\r
3984 promoChar = PieceToChar(BlackBishop);
\r
3986 case PB_Chancellor:
\r
3987 promoChar = PieceToChar(BlackMarshall);
\r
3989 case PB_Archbishop:
\r
3990 promoChar = PieceToChar(BlackAngel);
\r
3993 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
3998 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3999 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4000 only show the popup when we are already sure the move is valid or
\r
4001 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4002 will figure out it is a promotion from the promoChar. */
\r
4003 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4004 fromX = fromY = -1;
\r
4005 if (!appData.highlightLastMove) {
\r
4006 ClearHighlights();
\r
4007 DrawPosition(FALSE, NULL);
\r
4014 /* Pop up promotion dialog */
\r
4016 PromotionPopup(HWND hwnd)
\r
4020 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4021 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4022 hwnd, (DLGPROC)lpProc);
\r
4023 FreeProcInstance(lpProc);
\r
4029 DrawPosition(TRUE, NULL);
\r
4030 PromotionPopup(hwndMain);
\r
4033 /* Toggle ShowThinking */
\r
4035 ToggleShowThinking()
\r
4037 appData.showThinking = !appData.showThinking;
\r
4038 ShowThinkingEvent();
\r
4042 LoadGameDialog(HWND hwnd, char* title)
\r
4046 char fileTitle[MSG_SIZ];
\r
4047 f = OpenFileDialog(hwnd, "rb", "",
\r
4048 appData.oldSaveStyle ? "gam" : "pgn",
\r
4050 title, &number, fileTitle, NULL);
\r
4052 cmailMsgLoaded = FALSE;
\r
4053 if (number == 0) {
\r
4054 int error = GameListBuild(f);
\r
4056 DisplayError("Cannot build game list", error);
\r
4057 } else if (!ListEmpty(&gameList) &&
\r
4058 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4059 GameListPopUp(f, fileTitle);
\r
4062 GameListDestroy();
\r
4065 LoadGame(f, number, fileTitle, FALSE);
\r
4069 int get_term_width()
\r
4074 HFONT hfont, hold_font;
\r
4079 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4083 // get the text metrics
\r
4084 hdc = GetDC(hText);
\r
4085 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4086 if (consoleCF.dwEffects & CFE_BOLD)
\r
4087 lf.lfWeight = FW_BOLD;
\r
4088 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4089 lf.lfItalic = TRUE;
\r
4090 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4091 lf.lfStrikeOut = TRUE;
\r
4092 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4093 lf.lfUnderline = TRUE;
\r
4094 hfont = CreateFontIndirect(&lf);
\r
4095 hold_font = SelectObject(hdc, hfont);
\r
4096 GetTextMetrics(hdc, &tm);
\r
4097 SelectObject(hdc, hold_font);
\r
4098 DeleteObject(hfont);
\r
4099 ReleaseDC(hText, hdc);
\r
4101 // get the rectangle
\r
4102 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4104 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4107 void UpdateICSWidth(HWND hText)
\r
4109 LONG old_width, new_width;
\r
4111 new_width = get_term_width(hText, FALSE);
\r
4112 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4113 if (new_width != old_width)
\r
4115 ics_update_width(new_width);
\r
4116 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4121 ChangedConsoleFont()
\r
4124 CHARRANGE tmpsel, sel;
\r
4125 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4126 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4127 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4130 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4131 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4132 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4133 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4134 * size. This was undocumented in the version of MSVC++ that I had
\r
4135 * when I wrote the code, but is apparently documented now.
\r
4137 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4138 cfmt.bCharSet = f->lf.lfCharSet;
\r
4139 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4140 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4141 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4142 /* Why are the following seemingly needed too? */
\r
4143 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4144 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4145 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4147 tmpsel.cpMax = -1; /*999999?*/
\r
4148 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4149 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4150 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4151 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4153 paraf.cbSize = sizeof(paraf);
\r
4154 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4155 paraf.dxStartIndent = 0;
\r
4156 paraf.dxOffset = WRAP_INDENT;
\r
4157 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4158 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4159 UpdateICSWidth(hText);
\r
4162 /*---------------------------------------------------------------------------*\
\r
4164 * Window Proc for main window
\r
4166 \*---------------------------------------------------------------------------*/
\r
4168 /* Process messages for main window, etc. */
\r
4170 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4173 int wmId, wmEvent;
\r
4177 char fileTitle[MSG_SIZ];
\r
4178 char buf[MSG_SIZ];
\r
4179 static SnapData sd;
\r
4181 switch (message) {
\r
4183 case WM_PAINT: /* message: repaint portion of window */
\r
4187 case WM_ERASEBKGND:
\r
4188 if (IsIconic(hwnd)) {
\r
4189 /* Cheat; change the message */
\r
4190 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4192 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4196 case WM_LBUTTONDOWN:
\r
4197 case WM_MBUTTONDOWN:
\r
4198 case WM_RBUTTONDOWN:
\r
4199 case WM_LBUTTONUP:
\r
4200 case WM_MBUTTONUP:
\r
4201 case WM_RBUTTONUP:
\r
4202 case WM_MOUSEMOVE:
\r
4203 case WM_MOUSEWHEEL:
\r
4204 MouseEvent(hwnd, message, wParam, lParam);
\r
4207 JAWS_KB_NAVIGATION
\r
4211 JAWS_ALT_INTERCEPT
\r
4213 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4214 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4215 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4216 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4218 SendMessage(h, message, wParam, lParam);
\r
4219 } else if(lParam != KF_REPEAT) {
\r
4220 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4221 PopUpMoveDialog((char)wParam);
\r
4222 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4223 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4228 case WM_PALETTECHANGED:
\r
4229 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4231 HDC hdc = GetDC(hwndMain);
\r
4232 SelectPalette(hdc, hPal, TRUE);
\r
4233 nnew = RealizePalette(hdc);
\r
4235 paletteChanged = TRUE;
\r
4236 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4238 ReleaseDC(hwnd, hdc);
\r
4242 case WM_QUERYNEWPALETTE:
\r
4243 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4245 HDC hdc = GetDC(hwndMain);
\r
4246 paletteChanged = FALSE;
\r
4247 SelectPalette(hdc, hPal, FALSE);
\r
4248 nnew = RealizePalette(hdc);
\r
4250 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4252 ReleaseDC(hwnd, hdc);
\r
4257 case WM_COMMAND: /* message: command from application menu */
\r
4258 wmId = LOWORD(wParam);
\r
4259 wmEvent = HIWORD(wParam);
\r
4264 SAY("new game enter a move to play against the computer with white");
\r
4267 case IDM_NewGameFRC:
\r
4268 if( NewGameFRC() == 0 ) {
\r
4273 case IDM_NewVariant:
\r
4274 NewVariantPopup(hwnd);
\r
4277 case IDM_LoadGame:
\r
4278 LoadGameDialog(hwnd, "Load Game from File");
\r
4281 case IDM_LoadNextGame:
\r
4285 case IDM_LoadPrevGame:
\r
4289 case IDM_ReloadGame:
\r
4293 case IDM_LoadPosition:
\r
4294 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4295 Reset(FALSE, TRUE);
\r
4298 f = OpenFileDialog(hwnd, "rb", "",
\r
4299 appData.oldSaveStyle ? "pos" : "fen",
\r
4301 "Load Position from File", &number, fileTitle, NULL);
\r
4303 LoadPosition(f, number, fileTitle);
\r
4307 case IDM_LoadNextPosition:
\r
4308 ReloadPosition(1);
\r
4311 case IDM_LoadPrevPosition:
\r
4312 ReloadPosition(-1);
\r
4315 case IDM_ReloadPosition:
\r
4316 ReloadPosition(0);
\r
4319 case IDM_SaveGame:
\r
4320 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4321 f = OpenFileDialog(hwnd, "a", defName,
\r
4322 appData.oldSaveStyle ? "gam" : "pgn",
\r
4324 "Save Game to File", NULL, fileTitle, NULL);
\r
4326 SaveGame(f, 0, "");
\r
4330 case IDM_SavePosition:
\r
4331 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4332 f = OpenFileDialog(hwnd, "a", defName,
\r
4333 appData.oldSaveStyle ? "pos" : "fen",
\r
4335 "Save Position to File", NULL, fileTitle, NULL);
\r
4337 SavePosition(f, 0, "");
\r
4341 case IDM_SaveDiagram:
\r
4342 defName = "diagram";
\r
4343 f = OpenFileDialog(hwnd, "wb", defName,
\r
4346 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4352 case IDM_CopyGame:
\r
4353 CopyGameToClipboard();
\r
4356 case IDM_PasteGame:
\r
4357 PasteGameFromClipboard();
\r
4360 case IDM_CopyGameListToClipboard:
\r
4361 CopyGameListToClipboard();
\r
4364 /* [AS] Autodetect FEN or PGN data */
\r
4365 case IDM_PasteAny:
\r
4366 PasteGameOrFENFromClipboard();
\r
4369 /* [AS] Move history */
\r
4370 case IDM_ShowMoveHistory:
\r
4371 if( MoveHistoryIsUp() ) {
\r
4372 MoveHistoryPopDown();
\r
4375 MoveHistoryPopUp();
\r
4379 /* [AS] Eval graph */
\r
4380 case IDM_ShowEvalGraph:
\r
4381 if( EvalGraphIsUp() ) {
\r
4382 EvalGraphPopDown();
\r
4386 SetFocus(hwndMain);
\r
4390 /* [AS] Engine output */
\r
4391 case IDM_ShowEngineOutput:
\r
4392 if( EngineOutputIsUp() ) {
\r
4393 EngineOutputPopDown();
\r
4396 EngineOutputPopUp();
\r
4400 /* [AS] User adjudication */
\r
4401 case IDM_UserAdjudication_White:
\r
4402 UserAdjudicationEvent( +1 );
\r
4405 case IDM_UserAdjudication_Black:
\r
4406 UserAdjudicationEvent( -1 );
\r
4409 case IDM_UserAdjudication_Draw:
\r
4410 UserAdjudicationEvent( 0 );
\r
4413 /* [AS] Game list options dialog */
\r
4414 case IDM_GameListOptions:
\r
4415 GameListOptions();
\r
4422 case IDM_CopyPosition:
\r
4423 CopyFENToClipboard();
\r
4426 case IDM_PastePosition:
\r
4427 PasteFENFromClipboard();
\r
4430 case IDM_MailMove:
\r
4434 case IDM_ReloadCMailMsg:
\r
4435 Reset(TRUE, TRUE);
\r
4436 ReloadCmailMsgEvent(FALSE);
\r
4439 case IDM_Minimize:
\r
4440 ShowWindow(hwnd, SW_MINIMIZE);
\r
4447 case IDM_MachineWhite:
\r
4448 MachineWhiteEvent();
\r
4450 * refresh the tags dialog only if it's visible
\r
4452 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4454 tags = PGNTags(&gameInfo);
\r
4455 TagsPopUp(tags, CmailMsg());
\r
4458 SAY("computer starts playing white");
\r
4461 case IDM_MachineBlack:
\r
4462 MachineBlackEvent();
\r
4464 * refresh the tags dialog only if it's visible
\r
4466 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4468 tags = PGNTags(&gameInfo);
\r
4469 TagsPopUp(tags, CmailMsg());
\r
4472 SAY("computer starts playing black");
\r
4475 case IDM_TwoMachines:
\r
4476 TwoMachinesEvent();
\r
4478 * refresh the tags dialog only if it's visible
\r
4480 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4482 tags = PGNTags(&gameInfo);
\r
4483 TagsPopUp(tags, CmailMsg());
\r
4486 SAY("programs start playing each other");
\r
4489 case IDM_AnalysisMode:
\r
4490 if (!first.analysisSupport) {
\r
4491 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4492 DisplayError(buf, 0);
\r
4494 SAY("analyzing current position");
\r
4495 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4496 if (appData.icsActive) {
\r
4497 if (gameMode != IcsObserving) {
\r
4498 sprintf(buf, "You are not observing a game");
\r
4499 DisplayError(buf, 0);
\r
4500 /* secure check */
\r
4501 if (appData.icsEngineAnalyze) {
\r
4502 if (appData.debugMode)
\r
4503 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4504 ExitAnalyzeMode();
\r
4510 /* if enable, user want disable icsEngineAnalyze */
\r
4511 if (appData.icsEngineAnalyze) {
\r
4512 ExitAnalyzeMode();
\r
4516 appData.icsEngineAnalyze = TRUE;
\r
4517 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4520 if (!appData.showThinking) ToggleShowThinking();
\r
4521 AnalyzeModeEvent();
\r
4525 case IDM_AnalyzeFile:
\r
4526 if (!first.analysisSupport) {
\r
4527 char buf[MSG_SIZ];
\r
4528 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4529 DisplayError(buf, 0);
\r
4531 if (!appData.showThinking) ToggleShowThinking();
\r
4532 AnalyzeFileEvent();
\r
4533 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4534 AnalysisPeriodicEvent(1);
\r
4538 case IDM_IcsClient:
\r
4542 case IDM_EditGame:
\r
4547 case IDM_EditPosition:
\r
4548 EditPositionEvent();
\r
4549 SAY("to set up a position type a FEN");
\r
4552 case IDM_Training:
\r
4556 case IDM_ShowGameList:
\r
4557 ShowGameListProc();
\r
4560 case IDM_EditTags:
\r
4564 case IDM_EditComment:
\r
4565 if (commentUp && editComment) {
\r
4568 EditCommentEvent();
\r
4588 case IDM_CallFlag:
\r
4608 case IDM_StopObserving:
\r
4609 StopObservingEvent();
\r
4612 case IDM_StopExamining:
\r
4613 StopExaminingEvent();
\r
4616 case IDM_TypeInMove:
\r
4617 PopUpMoveDialog('\000');
\r
4620 case IDM_TypeInName:
\r
4621 PopUpNameDialog('\000');
\r
4624 case IDM_Backward:
\r
4626 SetFocus(hwndMain);
\r
4633 SetFocus(hwndMain);
\r
4638 SetFocus(hwndMain);
\r
4643 SetFocus(hwndMain);
\r
4650 case IDM_TruncateGame:
\r
4651 TruncateGameEvent();
\r
4658 case IDM_RetractMove:
\r
4659 RetractMoveEvent();
\r
4662 case IDM_FlipView:
\r
4663 flipView = !flipView;
\r
4664 DrawPosition(FALSE, NULL);
\r
4667 case IDM_FlipClock:
\r
4668 flipClock = !flipClock;
\r
4669 DisplayBothClocks();
\r
4670 DrawPosition(FALSE, NULL);
\r
4673 case IDM_MuteSounds:
\r
4674 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4675 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4676 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4679 case IDM_GeneralOptions:
\r
4680 GeneralOptionsPopup(hwnd);
\r
4681 DrawPosition(TRUE, NULL);
\r
4684 case IDM_BoardOptions:
\r
4685 BoardOptionsPopup(hwnd);
\r
4688 case IDM_EnginePlayOptions:
\r
4689 EnginePlayOptionsPopup(hwnd);
\r
4692 case IDM_Engine1Options:
\r
4693 EngineOptionsPopup(hwnd, &first);
\r
4696 case IDM_Engine2Options:
\r
4697 EngineOptionsPopup(hwnd, &second);
\r
4700 case IDM_OptionsUCI:
\r
4701 UciOptionsPopup(hwnd);
\r
4704 case IDM_IcsOptions:
\r
4705 IcsOptionsPopup(hwnd);
\r
4709 FontsOptionsPopup(hwnd);
\r
4713 SoundOptionsPopup(hwnd);
\r
4716 case IDM_CommPort:
\r
4717 CommPortOptionsPopup(hwnd);
\r
4720 case IDM_LoadOptions:
\r
4721 LoadOptionsPopup(hwnd);
\r
4724 case IDM_SaveOptions:
\r
4725 SaveOptionsPopup(hwnd);
\r
4728 case IDM_TimeControl:
\r
4729 TimeControlOptionsPopup(hwnd);
\r
4732 case IDM_SaveSettings:
\r
4733 SaveSettings(settingsFileName);
\r
4736 case IDM_SaveSettingsOnExit:
\r
4737 saveSettingsOnExit = !saveSettingsOnExit;
\r
4738 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4739 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4740 MF_CHECKED : MF_UNCHECKED));
\r
4751 case IDM_AboutGame:
\r
4756 appData.debugMode = !appData.debugMode;
\r
4757 if (appData.debugMode) {
\r
4758 char dir[MSG_SIZ];
\r
4759 GetCurrentDirectory(MSG_SIZ, dir);
\r
4760 SetCurrentDirectory(installDir);
\r
4761 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4762 SetCurrentDirectory(dir);
\r
4763 setbuf(debugFP, NULL);
\r
4770 case IDM_HELPCONTENTS:
\r
4771 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4772 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4773 MessageBox (GetFocus(),
\r
4774 "Unable to activate help",
\r
4775 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4779 case IDM_HELPSEARCH:
\r
4780 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4781 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4782 MessageBox (GetFocus(),
\r
4783 "Unable to activate help",
\r
4784 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4788 case IDM_HELPHELP:
\r
4789 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4790 MessageBox (GetFocus(),
\r
4791 "Unable to activate help",
\r
4792 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4797 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4799 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4800 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4801 FreeProcInstance(lpProc);
\r
4804 case IDM_DirectCommand1:
\r
4805 AskQuestionEvent("Direct Command",
\r
4806 "Send to chess program:", "", "1");
\r
4808 case IDM_DirectCommand2:
\r
4809 AskQuestionEvent("Direct Command",
\r
4810 "Send to second chess program:", "", "2");
\r
4813 case EP_WhitePawn:
\r
4814 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4815 fromX = fromY = -1;
\r
4818 case EP_WhiteKnight:
\r
4819 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4820 fromX = fromY = -1;
\r
4823 case EP_WhiteBishop:
\r
4824 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4825 fromX = fromY = -1;
\r
4828 case EP_WhiteRook:
\r
4829 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4830 fromX = fromY = -1;
\r
4833 case EP_WhiteQueen:
\r
4834 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4835 fromX = fromY = -1;
\r
4838 case EP_WhiteFerz:
\r
4839 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4840 fromX = fromY = -1;
\r
4843 case EP_WhiteWazir:
\r
4844 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4845 fromX = fromY = -1;
\r
4848 case EP_WhiteAlfil:
\r
4849 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4850 fromX = fromY = -1;
\r
4853 case EP_WhiteCannon:
\r
4854 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4855 fromX = fromY = -1;
\r
4858 case EP_WhiteCardinal:
\r
4859 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4860 fromX = fromY = -1;
\r
4863 case EP_WhiteMarshall:
\r
4864 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4865 fromX = fromY = -1;
\r
4868 case EP_WhiteKing:
\r
4869 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4870 fromX = fromY = -1;
\r
4873 case EP_BlackPawn:
\r
4874 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4875 fromX = fromY = -1;
\r
4878 case EP_BlackKnight:
\r
4879 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4880 fromX = fromY = -1;
\r
4883 case EP_BlackBishop:
\r
4884 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4885 fromX = fromY = -1;
\r
4888 case EP_BlackRook:
\r
4889 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4890 fromX = fromY = -1;
\r
4893 case EP_BlackQueen:
\r
4894 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4895 fromX = fromY = -1;
\r
4898 case EP_BlackFerz:
\r
4899 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4900 fromX = fromY = -1;
\r
4903 case EP_BlackWazir:
\r
4904 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4905 fromX = fromY = -1;
\r
4908 case EP_BlackAlfil:
\r
4909 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4910 fromX = fromY = -1;
\r
4913 case EP_BlackCannon:
\r
4914 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4915 fromX = fromY = -1;
\r
4918 case EP_BlackCardinal:
\r
4919 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4920 fromX = fromY = -1;
\r
4923 case EP_BlackMarshall:
\r
4924 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4925 fromX = fromY = -1;
\r
4928 case EP_BlackKing:
\r
4929 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4930 fromX = fromY = -1;
\r
4933 case EP_EmptySquare:
\r
4934 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4935 fromX = fromY = -1;
\r
4938 case EP_ClearBoard:
\r
4939 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4940 fromX = fromY = -1;
\r
4944 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4945 fromX = fromY = -1;
\r
4949 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4950 fromX = fromY = -1;
\r
4954 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4955 fromX = fromY = -1;
\r
4959 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4960 fromX = fromY = -1;
\r
4964 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4965 fromX = fromY = -1;
\r
4969 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4970 fromX = fromY = -1;
\r
4974 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4975 fromX = fromY = -1;
\r
4979 DropMenuEvent(WhiteRook, fromX, fromY);
\r
4980 fromX = fromY = -1;
\r
4984 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
4985 fromX = fromY = -1;
\r
4989 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4995 case CLOCK_TIMER_ID:
\r
4996 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
4997 clockTimerEvent = 0;
\r
4998 DecrementClocks(); /* call into back end */
\r
5000 case LOAD_GAME_TIMER_ID:
\r
5001 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5002 loadGameTimerEvent = 0;
\r
5003 AutoPlayGameLoop(); /* call into back end */
\r
5005 case ANALYSIS_TIMER_ID:
\r
5006 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5007 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5008 AnalysisPeriodicEvent(0);
\r
5010 KillTimer(hwnd, analysisTimerEvent);
\r
5011 analysisTimerEvent = 0;
\r
5014 case DELAYED_TIMER_ID:
\r
5015 KillTimer(hwnd, delayedTimerEvent);
\r
5016 delayedTimerEvent = 0;
\r
5017 delayedTimerCallback();
\r
5022 case WM_USER_Input:
\r
5023 InputEvent(hwnd, message, wParam, lParam);
\r
5026 /* [AS] Also move "attached" child windows */
\r
5027 case WM_WINDOWPOSCHANGING:
\r
5029 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5030 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5032 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5033 /* Window is moving */
\r
5036 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5037 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5038 rcMain.right = wpMain.x + wpMain.width;
\r
5039 rcMain.top = wpMain.y;
\r
5040 rcMain.bottom = wpMain.y + wpMain.height;
\r
5042 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5043 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5044 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5045 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5046 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5047 wpMain.x = lpwp->x;
\r
5048 wpMain.y = lpwp->y;
\r
5053 /* [AS] Snapping */
\r
5054 case WM_ENTERSIZEMOVE:
\r
5055 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5056 if (hwnd == hwndMain) {
\r
5057 doingSizing = TRUE;
\r
5060 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5064 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5065 if (hwnd == hwndMain) {
\r
5066 lastSizing = wParam;
\r
5071 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5072 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5074 case WM_EXITSIZEMOVE:
\r
5075 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5076 if (hwnd == hwndMain) {
\r
5078 doingSizing = FALSE;
\r
5079 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5080 GetClientRect(hwnd, &client);
\r
5081 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5083 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5085 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5088 case WM_DESTROY: /* message: window being destroyed */
\r
5089 PostQuitMessage(0);
\r
5093 if (hwnd == hwndMain) {
\r
5098 default: /* Passes it on if unprocessed */
\r
5099 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5104 /*---------------------------------------------------------------------------*\
\r
5106 * Misc utility routines
\r
5108 \*---------------------------------------------------------------------------*/
\r
5111 * Decent random number generator, at least not as bad as Windows
\r
5112 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5114 unsigned int randstate;
\r
5119 randstate = randstate * 1664525 + 1013904223;
\r
5120 return (int) randstate & 0x7fffffff;
\r
5124 mysrandom(unsigned int seed)
\r
5131 * returns TRUE if user selects a different color, FALSE otherwise
\r
5135 ChangeColor(HWND hwnd, COLORREF *which)
\r
5137 static BOOL firstTime = TRUE;
\r
5138 static DWORD customColors[16];
\r
5140 COLORREF newcolor;
\r
5145 /* Make initial colors in use available as custom colors */
\r
5146 /* Should we put the compiled-in defaults here instead? */
\r
5148 customColors[i++] = lightSquareColor & 0xffffff;
\r
5149 customColors[i++] = darkSquareColor & 0xffffff;
\r
5150 customColors[i++] = whitePieceColor & 0xffffff;
\r
5151 customColors[i++] = blackPieceColor & 0xffffff;
\r
5152 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5153 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5155 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5156 customColors[i++] = textAttribs[ccl].color;
\r
5158 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5159 firstTime = FALSE;
\r
5162 cc.lStructSize = sizeof(cc);
\r
5163 cc.hwndOwner = hwnd;
\r
5164 cc.hInstance = NULL;
\r
5165 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5166 cc.lpCustColors = (LPDWORD) customColors;
\r
5167 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5169 if (!ChooseColor(&cc)) return FALSE;
\r
5171 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5172 if (newcolor == *which) return FALSE;
\r
5173 *which = newcolor;
\r
5177 InitDrawingColors();
\r
5178 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5183 MyLoadSound(MySound *ms)
\r
5189 if (ms->data) free(ms->data);
\r
5192 switch (ms->name[0]) {
\r
5198 /* System sound from Control Panel. Don't preload here. */
\r
5202 if (ms->name[1] == NULLCHAR) {
\r
5203 /* "!" alone = silence */
\r
5206 /* Builtin wave resource. Error if not found. */
\r
5207 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5208 if (h == NULL) break;
\r
5209 ms->data = (void *)LoadResource(hInst, h);
\r
5210 if (h == NULL) break;
\r
5215 /* .wav file. Error if not found. */
\r
5216 f = fopen(ms->name, "rb");
\r
5217 if (f == NULL) break;
\r
5218 if (fstat(fileno(f), &st) < 0) break;
\r
5219 ms->data = malloc(st.st_size);
\r
5220 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5226 char buf[MSG_SIZ];
\r
5227 sprintf(buf, "Error loading sound %s", ms->name);
\r
5228 DisplayError(buf, GetLastError());
\r
5234 MyPlaySound(MySound *ms)
\r
5236 BOOLEAN ok = FALSE;
\r
5238 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5239 switch (ms->name[0]) {
\r
5241 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5246 /* System sound from Control Panel (deprecated feature).
\r
5247 "$" alone or an unset sound name gets default beep (still in use). */
\r
5248 if (ms->name[1]) {
\r
5249 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5251 if (!ok) ok = MessageBeep(MB_OK);
\r
5254 /* Builtin wave resource, or "!" alone for silence */
\r
5255 if (ms->name[1]) {
\r
5256 if (ms->data == NULL) return FALSE;
\r
5257 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5263 /* .wav file. Error if not found. */
\r
5264 if (ms->data == NULL) return FALSE;
\r
5265 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5268 /* Don't print an error: this can happen innocently if the sound driver
\r
5269 is busy; for instance, if another instance of WinBoard is playing
\r
5270 a sound at about the same time. */
\r
5276 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5279 OPENFILENAME *ofn;
\r
5280 static UINT *number; /* gross that this is static */
\r
5282 switch (message) {
\r
5283 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5284 /* Center the dialog over the application window */
\r
5285 ofn = (OPENFILENAME *) lParam;
\r
5286 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5287 number = (UINT *) ofn->lCustData;
\r
5288 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5292 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5293 return FALSE; /* Allow for further processing */
\r
5296 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5297 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5299 return FALSE; /* Allow for further processing */
\r
5305 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5307 static UINT *number;
\r
5308 OPENFILENAME *ofname;
\r
5311 case WM_INITDIALOG:
\r
5312 ofname = (OPENFILENAME *)lParam;
\r
5313 number = (UINT *)(ofname->lCustData);
\r
5316 ofnot = (OFNOTIFY *)lParam;
\r
5317 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5318 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5327 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5328 char *nameFilt, char *dlgTitle, UINT *number,
\r
5329 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5331 OPENFILENAME openFileName;
\r
5332 char buf1[MSG_SIZ];
\r
5335 if (fileName == NULL) fileName = buf1;
\r
5336 if (defName == NULL) {
\r
5337 strcpy(fileName, "*.");
\r
5338 strcat(fileName, defExt);
\r
5340 strcpy(fileName, defName);
\r
5342 if (fileTitle) strcpy(fileTitle, "");
\r
5343 if (number) *number = 0;
\r
5345 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5346 openFileName.hwndOwner = hwnd;
\r
5347 openFileName.hInstance = (HANDLE) hInst;
\r
5348 openFileName.lpstrFilter = nameFilt;
\r
5349 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5350 openFileName.nMaxCustFilter = 0L;
\r
5351 openFileName.nFilterIndex = 1L;
\r
5352 openFileName.lpstrFile = fileName;
\r
5353 openFileName.nMaxFile = MSG_SIZ;
\r
5354 openFileName.lpstrFileTitle = fileTitle;
\r
5355 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5356 openFileName.lpstrInitialDir = NULL;
\r
5357 openFileName.lpstrTitle = dlgTitle;
\r
5358 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5359 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5360 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5361 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5362 openFileName.nFileOffset = 0;
\r
5363 openFileName.nFileExtension = 0;
\r
5364 openFileName.lpstrDefExt = defExt;
\r
5365 openFileName.lCustData = (LONG) number;
\r
5366 openFileName.lpfnHook = oldDialog ?
\r
5367 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5368 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5370 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5371 GetOpenFileName(&openFileName)) {
\r
5372 /* open the file */
\r
5373 f = fopen(openFileName.lpstrFile, write);
\r
5375 MessageBox(hwnd, "File open failed", NULL,
\r
5376 MB_OK|MB_ICONEXCLAMATION);
\r
5380 int err = CommDlgExtendedError();
\r
5381 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5390 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5392 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5395 * Get the first pop-up menu in the menu template. This is the
\r
5396 * menu that TrackPopupMenu displays.
\r
5398 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5400 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5403 * TrackPopup uses screen coordinates, so convert the
\r
5404 * coordinates of the mouse click to screen coordinates.
\r
5406 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5408 /* Draw and track the floating pop-up menu. */
\r
5409 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5410 pt.x, pt.y, 0, hwnd, NULL);
\r
5412 /* Destroy the menu.*/
\r
5413 DestroyMenu(hmenu);
\r
5418 int sizeX, sizeY, newSizeX, newSizeY;
\r
5420 } ResizeEditPlusButtonsClosure;
\r
5423 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5425 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5429 if (hChild == cl->hText) return TRUE;
\r
5430 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5431 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5432 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5433 ScreenToClient(cl->hDlg, &pt);
\r
5434 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5435 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5439 /* Resize a dialog that has a (rich) edit field filling most of
\r
5440 the top, with a row of buttons below */
\r
5442 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5445 int newTextHeight, newTextWidth;
\r
5446 ResizeEditPlusButtonsClosure cl;
\r
5448 /*if (IsIconic(hDlg)) return;*/
\r
5449 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5451 cl.hdwp = BeginDeferWindowPos(8);
\r
5453 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5454 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5455 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5456 if (newTextHeight < 0) {
\r
5457 newSizeY += -newTextHeight;
\r
5458 newTextHeight = 0;
\r
5460 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5461 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5467 cl.newSizeX = newSizeX;
\r
5468 cl.newSizeY = newSizeY;
\r
5469 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5471 EndDeferWindowPos(cl.hdwp);
\r
5474 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5476 RECT rChild, rParent;
\r
5477 int wChild, hChild, wParent, hParent;
\r
5478 int wScreen, hScreen, xNew, yNew;
\r
5481 /* Get the Height and Width of the child window */
\r
5482 GetWindowRect (hwndChild, &rChild);
\r
5483 wChild = rChild.right - rChild.left;
\r
5484 hChild = rChild.bottom - rChild.top;
\r
5486 /* Get the Height and Width of the parent window */
\r
5487 GetWindowRect (hwndParent, &rParent);
\r
5488 wParent = rParent.right - rParent.left;
\r
5489 hParent = rParent.bottom - rParent.top;
\r
5491 /* Get the display limits */
\r
5492 hdc = GetDC (hwndChild);
\r
5493 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5494 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5495 ReleaseDC(hwndChild, hdc);
\r
5497 /* Calculate new X position, then adjust for screen */
\r
5498 xNew = rParent.left + ((wParent - wChild) /2);
\r
5501 } else if ((xNew+wChild) > wScreen) {
\r
5502 xNew = wScreen - wChild;
\r
5505 /* Calculate new Y position, then adjust for screen */
\r
5507 yNew = rParent.top + ((hParent - hChild) /2);
\r
5510 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5515 } else if ((yNew+hChild) > hScreen) {
\r
5516 yNew = hScreen - hChild;
\r
5519 /* Set it, and return */
\r
5520 return SetWindowPos (hwndChild, NULL,
\r
5521 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5524 /* Center one window over another */
\r
5525 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5527 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5530 /*---------------------------------------------------------------------------*\
\r
5532 * Startup Dialog functions
\r
5534 \*---------------------------------------------------------------------------*/
\r
5536 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5538 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5540 while (*cd != NULL) {
\r
5541 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5547 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5549 char buf1[MAX_ARG_LEN];
\r
5552 if (str[0] == '@') {
\r
5553 FILE* f = fopen(str + 1, "r");
\r
5555 DisplayFatalError(str + 1, errno, 2);
\r
5558 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5560 buf1[len] = NULLCHAR;
\r
5564 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5567 char buf[MSG_SIZ];
\r
5568 char *end = strchr(str, '\n');
\r
5569 if (end == NULL) return;
\r
5570 memcpy(buf, str, end - str);
\r
5571 buf[end - str] = NULLCHAR;
\r
5572 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5578 SetStartupDialogEnables(HWND hDlg)
\r
5580 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5581 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5582 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5583 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5584 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5585 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5586 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5587 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5588 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5589 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5590 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5591 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5592 IsDlgButtonChecked(hDlg, OPT_View));
\r
5596 QuoteForFilename(char *filename)
\r
5598 int dquote, space;
\r
5599 dquote = strchr(filename, '"') != NULL;
\r
5600 space = strchr(filename, ' ') != NULL;
\r
5601 if (dquote || space) {
\r
5613 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5615 char buf[MSG_SIZ];
\r
5618 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5619 q = QuoteForFilename(nthcp);
\r
5620 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5621 if (*nthdir != NULLCHAR) {
\r
5622 q = QuoteForFilename(nthdir);
\r
5623 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5625 if (*nthcp == NULLCHAR) {
\r
5626 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5627 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5628 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5629 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5634 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5636 char buf[MSG_SIZ];
\r
5640 switch (message) {
\r
5641 case WM_INITDIALOG:
\r
5642 /* Center the dialog */
\r
5643 CenterWindow (hDlg, GetDesktopWindow());
\r
5644 /* Initialize the dialog items */
\r
5645 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5646 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5647 firstChessProgramNames);
\r
5648 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5649 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5650 secondChessProgramNames);
\r
5651 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5652 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5653 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5654 if (*appData.icsHelper != NULLCHAR) {
\r
5655 char *q = QuoteForFilename(appData.icsHelper);
\r
5656 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5658 if (*appData.icsHost == NULLCHAR) {
\r
5659 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5660 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5661 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5662 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5663 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5666 if (appData.icsActive) {
\r
5667 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5669 else if (appData.noChessProgram) {
\r
5670 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5673 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5676 SetStartupDialogEnables(hDlg);
\r
5680 switch (LOWORD(wParam)) {
\r
5682 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5683 strcpy(buf, "/fcp=");
\r
5684 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5686 ParseArgs(StringGet, &p);
\r
5687 strcpy(buf, "/scp=");
\r
5688 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5690 ParseArgs(StringGet, &p);
\r
5691 appData.noChessProgram = FALSE;
\r
5692 appData.icsActive = FALSE;
\r
5693 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5694 strcpy(buf, "/ics /icshost=");
\r
5695 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5697 ParseArgs(StringGet, &p);
\r
5698 if (appData.zippyPlay) {
\r
5699 strcpy(buf, "/fcp=");
\r
5700 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5702 ParseArgs(StringGet, &p);
\r
5704 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5705 appData.noChessProgram = TRUE;
\r
5706 appData.icsActive = FALSE;
\r
5708 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5709 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5712 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5713 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5715 ParseArgs(StringGet, &p);
\r
5717 EndDialog(hDlg, TRUE);
\r
5724 case IDM_HELPCONTENTS:
\r
5725 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5726 MessageBox (GetFocus(),
\r
5727 "Unable to activate help",
\r
5728 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5733 SetStartupDialogEnables(hDlg);
\r
5741 /*---------------------------------------------------------------------------*\
\r
5743 * About box dialog functions
\r
5745 \*---------------------------------------------------------------------------*/
\r
5747 /* Process messages for "About" dialog box */
\r
5749 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5751 switch (message) {
\r
5752 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5753 /* Center the dialog over the application window */
\r
5754 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5755 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5759 case WM_COMMAND: /* message: received a command */
\r
5760 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5761 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5762 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5770 /*---------------------------------------------------------------------------*\
\r
5772 * Comment Dialog functions
\r
5774 \*---------------------------------------------------------------------------*/
\r
5777 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5779 static HANDLE hwndText = NULL;
\r
5780 int len, newSizeX, newSizeY, flags;
\r
5781 static int sizeX, sizeY;
\r
5786 switch (message) {
\r
5787 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5788 /* Initialize the dialog items */
\r
5789 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5790 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5791 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5792 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5793 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5794 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5795 SetWindowText(hDlg, commentTitle);
\r
5796 if (editComment) {
\r
5797 SetFocus(hwndText);
\r
5799 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5801 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5802 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5803 MAKELPARAM(FALSE, 0));
\r
5804 /* Size and position the dialog */
\r
5805 if (!commentDialog) {
\r
5806 commentDialog = hDlg;
\r
5807 flags = SWP_NOZORDER;
\r
5808 GetClientRect(hDlg, &rect);
\r
5809 sizeX = rect.right;
\r
5810 sizeY = rect.bottom;
\r
5811 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5812 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5813 WINDOWPLACEMENT wp;
\r
5814 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5815 wp.length = sizeof(WINDOWPLACEMENT);
\r
5817 wp.showCmd = SW_SHOW;
\r
5818 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5819 wp.rcNormalPosition.left = wpComment.x;
\r
5820 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5821 wp.rcNormalPosition.top = wpComment.y;
\r
5822 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5823 SetWindowPlacement(hDlg, &wp);
\r
5825 GetClientRect(hDlg, &rect);
\r
5826 newSizeX = rect.right;
\r
5827 newSizeY = rect.bottom;
\r
5828 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5829 newSizeX, newSizeY);
\r
5836 case WM_COMMAND: /* message: received a command */
\r
5837 switch (LOWORD(wParam)) {
\r
5839 if (editComment) {
\r
5841 /* Read changed options from the dialog box */
\r
5842 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5843 len = GetWindowTextLength(hwndText);
\r
5844 str = (char *) malloc(len + 1);
\r
5845 GetWindowText(hwndText, str, len + 1);
\r
5854 ReplaceComment(commentIndex, str);
\r
5861 case OPT_CancelComment:
\r
5865 case OPT_ClearComment:
\r
5866 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5869 case OPT_EditComment:
\r
5870 EditCommentEvent();
\r
5879 newSizeX = LOWORD(lParam);
\r
5880 newSizeY = HIWORD(lParam);
\r
5881 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5886 case WM_GETMINMAXINFO:
\r
5887 /* Prevent resizing window too small */
\r
5888 mmi = (MINMAXINFO *) lParam;
\r
5889 mmi->ptMinTrackSize.x = 100;
\r
5890 mmi->ptMinTrackSize.y = 100;
\r
5897 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5902 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5904 if (str == NULL) str = "";
\r
5905 p = (char *) malloc(2 * strlen(str) + 2);
\r
5908 if (*str == '\n') *q++ = '\r';
\r
5912 if (commentText != NULL) free(commentText);
\r
5914 commentIndex = index;
\r
5915 commentTitle = title;
\r
5917 editComment = edit;
\r
5919 if (commentDialog) {
\r
5920 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5921 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5923 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5924 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5925 hwndMain, (DLGPROC)lpProc);
\r
5926 FreeProcInstance(lpProc);
\r
5932 /*---------------------------------------------------------------------------*\
\r
5934 * Type-in move dialog functions
\r
5936 \*---------------------------------------------------------------------------*/
\r
5939 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5941 char move[MSG_SIZ];
\r
5943 ChessMove moveType;
\r
5944 int fromX, fromY, toX, toY;
\r
5947 switch (message) {
\r
5948 case WM_INITDIALOG:
\r
5949 move[0] = (char) lParam;
\r
5950 move[1] = NULLCHAR;
\r
5951 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5952 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5953 SetWindowText(hInput, move);
\r
5955 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5959 switch (LOWORD(wParam)) {
\r
5961 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5962 { int n; Board board;
\r
5964 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5965 EditPositionPasteFEN(move);
\r
5966 EndDialog(hDlg, TRUE);
\r
5969 // [HGM] movenum: allow move number to be typed in any mode
\r
5970 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5972 EndDialog(hDlg, TRUE);
\r
5976 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5977 gameMode != Training) {
\r
5978 DisplayMoveError("Displayed move is not current");
\r
5980 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
5981 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5982 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
5983 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
5984 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
5985 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
5986 if (gameMode != Training)
\r
5987 forwardMostMove = currentMove;
\r
5988 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
5990 DisplayMoveError("Could not parse move");
\r
5993 EndDialog(hDlg, TRUE);
\r
5996 EndDialog(hDlg, FALSE);
\r
6007 PopUpMoveDialog(char firstchar)
\r
6011 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6012 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6013 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6014 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6015 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6016 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6017 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6018 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6019 gameMode == Training) {
\r
6020 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6021 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6022 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6023 FreeProcInstance(lpProc);
\r
6027 /*---------------------------------------------------------------------------*\
\r
6029 * Type-in name dialog functions
\r
6031 \*---------------------------------------------------------------------------*/
\r
6034 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6036 char move[MSG_SIZ];
\r
6039 switch (message) {
\r
6040 case WM_INITDIALOG:
\r
6041 move[0] = (char) lParam;
\r
6042 move[1] = NULLCHAR;
\r
6043 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6044 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6045 SetWindowText(hInput, move);
\r
6047 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6051 switch (LOWORD(wParam)) {
\r
6053 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6054 appData.userName = strdup(move);
\r
6057 EndDialog(hDlg, TRUE);
\r
6060 EndDialog(hDlg, FALSE);
\r
6071 PopUpNameDialog(char firstchar)
\r
6075 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6076 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6077 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6078 FreeProcInstance(lpProc);
\r
6081 /*---------------------------------------------------------------------------*\
\r
6085 \*---------------------------------------------------------------------------*/
\r
6087 /* Nonmodal error box */
\r
6088 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6089 WPARAM wParam, LPARAM lParam);
\r
6092 ErrorPopUp(char *title, char *content)
\r
6096 BOOLEAN modal = hwndMain == NULL;
\r
6114 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6115 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6118 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6120 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6121 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6122 hwndMain, (DLGPROC)lpProc);
\r
6123 FreeProcInstance(lpProc);
\r
6130 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6131 if (errorDialog == NULL) return;
\r
6132 DestroyWindow(errorDialog);
\r
6133 errorDialog = NULL;
\r
6134 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6138 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6143 switch (message) {
\r
6144 case WM_INITDIALOG:
\r
6145 GetWindowRect(hDlg, &rChild);
\r
6148 SetWindowPos(hDlg, NULL, rChild.left,
\r
6149 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6150 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6154 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6155 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6156 and it doesn't work when you resize the dialog.
\r
6157 For now, just give it a default position.
\r
6159 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6161 errorDialog = hDlg;
\r
6162 SetWindowText(hDlg, errorTitle);
\r
6163 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6164 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6168 switch (LOWORD(wParam)) {
\r
6171 if (errorDialog == hDlg) errorDialog = NULL;
\r
6172 DestroyWindow(hDlg);
\r
6184 HWND gothicDialog = NULL;
\r
6187 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6191 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6193 switch (message) {
\r
6194 case WM_INITDIALOG:
\r
6195 GetWindowRect(hDlg, &rChild);
\r
6197 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6201 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6202 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6203 and it doesn't work when you resize the dialog.
\r
6204 For now, just give it a default position.
\r
6206 gothicDialog = hDlg;
\r
6207 SetWindowText(hDlg, errorTitle);
\r
6208 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6209 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6213 switch (LOWORD(wParam)) {
\r
6216 if (errorDialog == hDlg) errorDialog = NULL;
\r
6217 DestroyWindow(hDlg);
\r
6229 GothicPopUp(char *title, VariantClass variant)
\r
6232 static char *lastTitle;
\r
6234 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6235 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6237 if(lastTitle != title && gothicDialog != NULL) {
\r
6238 DestroyWindow(gothicDialog);
\r
6239 gothicDialog = NULL;
\r
6241 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6242 title = lastTitle;
\r
6243 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6244 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6245 hwndMain, (DLGPROC)lpProc);
\r
6246 FreeProcInstance(lpProc);
\r
6251 /*---------------------------------------------------------------------------*\
\r
6253 * Ics Interaction console functions
\r
6255 \*---------------------------------------------------------------------------*/
\r
6257 #define HISTORY_SIZE 64
\r
6258 static char *history[HISTORY_SIZE];
\r
6259 int histIn = 0, histP = 0;
\r
6262 SaveInHistory(char *cmd)
\r
6264 if (history[histIn] != NULL) {
\r
6265 free(history[histIn]);
\r
6266 history[histIn] = NULL;
\r
6268 if (*cmd == NULLCHAR) return;
\r
6269 history[histIn] = StrSave(cmd);
\r
6270 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6271 if (history[histIn] != NULL) {
\r
6272 free(history[histIn]);
\r
6273 history[histIn] = NULL;
\r
6279 PrevInHistory(char *cmd)
\r
6282 if (histP == histIn) {
\r
6283 if (history[histIn] != NULL) free(history[histIn]);
\r
6284 history[histIn] = StrSave(cmd);
\r
6286 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6287 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6289 return history[histP];
\r
6295 if (histP == histIn) return NULL;
\r
6296 histP = (histP + 1) % HISTORY_SIZE;
\r
6297 return history[histP];
\r
6301 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6305 hmenu = LoadMenu(hInst, "TextMenu");
\r
6306 h = GetSubMenu(hmenu, 0);
\r
6308 if (strcmp(e->item, "-") == 0) {
\r
6309 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6311 if (e->item[0] == '|') {
\r
6312 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
\r
6313 IDM_CommandX + i, &e->item[1]);
\r
6315 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
\r
6324 WNDPROC consoleTextWindowProc;
\r
6327 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6329 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6330 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6334 SetWindowText(hInput, command);
\r
6336 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6338 sel.cpMin = 999999;
\r
6339 sel.cpMax = 999999;
\r
6340 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6345 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6346 if (sel.cpMin == sel.cpMax) {
\r
6347 /* Expand to surrounding word */
\r
6350 tr.chrg.cpMax = sel.cpMin;
\r
6351 tr.chrg.cpMin = --sel.cpMin;
\r
6352 if (sel.cpMin < 0) break;
\r
6353 tr.lpstrText = name;
\r
6354 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6355 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6359 tr.chrg.cpMin = sel.cpMax;
\r
6360 tr.chrg.cpMax = ++sel.cpMax;
\r
6361 tr.lpstrText = name;
\r
6362 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6363 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6366 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6367 MessageBeep(MB_ICONEXCLAMATION);
\r
6371 tr.lpstrText = name;
\r
6372 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6374 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6375 MessageBeep(MB_ICONEXCLAMATION);
\r
6378 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6381 sprintf(buf, "%s %s", command, name);
\r
6382 SetWindowText(hInput, buf);
\r
6383 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6385 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6386 SetWindowText(hInput, buf);
\r
6387 sel.cpMin = 999999;
\r
6388 sel.cpMax = 999999;
\r
6389 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6395 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6400 switch (message) {
\r
6402 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6405 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6408 sel.cpMin = 999999;
\r
6409 sel.cpMax = 999999;
\r
6410 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6411 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6416 if(wParam != '\022') {
\r
6417 if (wParam == '\t') {
\r
6418 if (GetKeyState(VK_SHIFT) < 0) {
\r
6420 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6421 if (buttonDesc[0].hwnd) {
\r
6422 SetFocus(buttonDesc[0].hwnd);
\r
6424 SetFocus(hwndMain);
\r
6428 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6431 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6432 JAWS_DELETE( SetFocus(hInput); )
\r
6433 SendMessage(hInput, message, wParam, lParam);
\r
6436 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6437 case WM_RBUTTONUP:
\r
6438 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6439 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6440 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6443 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6444 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6445 if (sel.cpMin == sel.cpMax) {
\r
6446 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6447 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6449 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6450 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6452 pt.x = LOWORD(lParam);
\r
6453 pt.y = HIWORD(lParam);
\r
6454 MenuPopup(hwnd, pt, hmenu, -1);
\r
6458 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6460 return SendMessage(hInput, message, wParam, lParam);
\r
6461 case WM_MBUTTONDOWN:
\r
6462 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6463 case WM_RBUTTONDOWN:
\r
6464 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6465 /* Move selection here if it was empty */
\r
6467 pt.x = LOWORD(lParam);
\r
6468 pt.y = HIWORD(lParam);
\r
6469 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6470 if (sel.cpMin == sel.cpMax) {
\r
6471 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6472 sel.cpMax = sel.cpMin;
\r
6473 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6475 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6479 switch (LOWORD(wParam)) {
\r
6480 case IDM_QuickPaste:
\r
6482 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6483 if (sel.cpMin == sel.cpMax) {
\r
6484 MessageBeep(MB_ICONEXCLAMATION);
\r
6487 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6488 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6489 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6494 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6497 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6500 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6504 int i = LOWORD(wParam) - IDM_CommandX;
\r
6505 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6506 icsTextMenuEntry[i].command != NULL) {
\r
6507 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6508 icsTextMenuEntry[i].getname,
\r
6509 icsTextMenuEntry[i].immediate);
\r
6517 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6520 WNDPROC consoleInputWindowProc;
\r
6523 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6525 char buf[MSG_SIZ];
\r
6527 static BOOL sendNextChar = FALSE;
\r
6528 static BOOL quoteNextChar = FALSE;
\r
6529 InputSource *is = consoleInputSource;
\r
6533 switch (message) {
\r
6535 if (!appData.localLineEditing || sendNextChar) {
\r
6536 is->buf[0] = (CHAR) wParam;
\r
6538 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6539 sendNextChar = FALSE;
\r
6542 if (quoteNextChar) {
\r
6543 buf[0] = (char) wParam;
\r
6544 buf[1] = NULLCHAR;
\r
6545 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6546 quoteNextChar = FALSE;
\r
6550 case '\r': /* Enter key */
\r
6551 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6552 if (consoleEcho) SaveInHistory(is->buf);
\r
6553 is->buf[is->count++] = '\n';
\r
6554 is->buf[is->count] = NULLCHAR;
\r
6555 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6556 if (consoleEcho) {
\r
6557 ConsoleOutput(is->buf, is->count, TRUE);
\r
6558 } else if (appData.localLineEditing) {
\r
6559 ConsoleOutput("\n", 1, TRUE);
\r
6562 case '\033': /* Escape key */
\r
6563 SetWindowText(hwnd, "");
\r
6564 cf.cbSize = sizeof(CHARFORMAT);
\r
6565 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6566 if (consoleEcho) {
\r
6567 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6569 cf.crTextColor = COLOR_ECHOOFF;
\r
6571 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6572 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6574 case '\t': /* Tab key */
\r
6575 if (GetKeyState(VK_SHIFT) < 0) {
\r
6577 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6580 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6581 if (buttonDesc[0].hwnd) {
\r
6582 SetFocus(buttonDesc[0].hwnd);
\r
6584 SetFocus(hwndMain);
\r
6588 case '\023': /* Ctrl+S */
\r
6589 sendNextChar = TRUE;
\r
6591 case '\021': /* Ctrl+Q */
\r
6592 quoteNextChar = TRUE;
\r
6602 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6603 p = PrevInHistory(buf);
\r
6605 SetWindowText(hwnd, p);
\r
6606 sel.cpMin = 999999;
\r
6607 sel.cpMax = 999999;
\r
6608 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6613 p = NextInHistory();
\r
6615 SetWindowText(hwnd, p);
\r
6616 sel.cpMin = 999999;
\r
6617 sel.cpMax = 999999;
\r
6618 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6624 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6628 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6632 case WM_MBUTTONDOWN:
\r
6633 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6634 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6636 case WM_RBUTTONUP:
\r
6637 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6638 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6639 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6643 hmenu = LoadMenu(hInst, "InputMenu");
\r
6644 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6645 if (sel.cpMin == sel.cpMax) {
\r
6646 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6647 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6649 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6650 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6652 pt.x = LOWORD(lParam);
\r
6653 pt.y = HIWORD(lParam);
\r
6654 MenuPopup(hwnd, pt, hmenu, -1);
\r
6658 switch (LOWORD(wParam)) {
\r
6660 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6662 case IDM_SelectAll:
\r
6664 sel.cpMax = -1; /*999999?*/
\r
6665 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6668 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6671 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6674 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6679 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6682 #define CO_MAX 100000
\r
6683 #define CO_TRIM 1000
\r
6686 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6688 static SnapData sd;
\r
6689 HWND hText, hInput;
\r
6691 static int sizeX, sizeY;
\r
6692 int newSizeX, newSizeY;
\r
6696 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6697 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6699 switch (message) {
\r
6701 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6703 ENLINK *pLink = (ENLINK*)lParam;
\r
6704 if (pLink->msg == WM_LBUTTONUP)
\r
6708 tr.chrg = pLink->chrg;
\r
6709 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6710 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6711 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6712 free(tr.lpstrText);
\r
6716 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6717 hwndConsole = hDlg;
\r
6719 consoleTextWindowProc = (WNDPROC)
\r
6720 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6721 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6722 consoleInputWindowProc = (WNDPROC)
\r
6723 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6724 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6725 Colorize(ColorNormal, TRUE);
\r
6726 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6727 ChangedConsoleFont();
\r
6728 GetClientRect(hDlg, &rect);
\r
6729 sizeX = rect.right;
\r
6730 sizeY = rect.bottom;
\r
6731 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6732 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6733 WINDOWPLACEMENT wp;
\r
6734 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6735 wp.length = sizeof(WINDOWPLACEMENT);
\r
6737 wp.showCmd = SW_SHOW;
\r
6738 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6739 wp.rcNormalPosition.left = wpConsole.x;
\r
6740 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6741 wp.rcNormalPosition.top = wpConsole.y;
\r
6742 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6743 SetWindowPlacement(hDlg, &wp);
\r
6746 // [HGM] Chessknight's change 2004-07-13
\r
6747 else { /* Determine Defaults */
\r
6748 WINDOWPLACEMENT wp;
\r
6749 wpConsole.x = wpMain.width + 1;
\r
6750 wpConsole.y = wpMain.y;
\r
6751 wpConsole.width = screenWidth - wpMain.width;
\r
6752 wpConsole.height = wpMain.height;
\r
6753 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6754 wp.length = sizeof(WINDOWPLACEMENT);
\r
6756 wp.showCmd = SW_SHOW;
\r
6757 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6758 wp.rcNormalPosition.left = wpConsole.x;
\r
6759 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6760 wp.rcNormalPosition.top = wpConsole.y;
\r
6761 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6762 SetWindowPlacement(hDlg, &wp);
\r
6765 // Allow hText to highlight URLs and send notifications on them
\r
6766 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6767 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6768 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6769 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6783 if (IsIconic(hDlg)) break;
\r
6784 newSizeX = LOWORD(lParam);
\r
6785 newSizeY = HIWORD(lParam);
\r
6786 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6787 RECT rectText, rectInput;
\r
6789 int newTextHeight, newTextWidth;
\r
6790 GetWindowRect(hText, &rectText);
\r
6791 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6792 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6793 if (newTextHeight < 0) {
\r
6794 newSizeY += -newTextHeight;
\r
6795 newTextHeight = 0;
\r
6797 SetWindowPos(hText, NULL, 0, 0,
\r
6798 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6799 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6800 pt.x = rectInput.left;
\r
6801 pt.y = rectInput.top + newSizeY - sizeY;
\r
6802 ScreenToClient(hDlg, &pt);
\r
6803 SetWindowPos(hInput, NULL,
\r
6804 pt.x, pt.y, /* needs client coords */
\r
6805 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6806 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6812 case WM_GETMINMAXINFO:
\r
6813 /* Prevent resizing window too small */
\r
6814 mmi = (MINMAXINFO *) lParam;
\r
6815 mmi->ptMinTrackSize.x = 100;
\r
6816 mmi->ptMinTrackSize.y = 100;
\r
6819 /* [AS] Snapping */
\r
6820 case WM_ENTERSIZEMOVE:
\r
6821 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6824 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6827 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6829 case WM_EXITSIZEMOVE:
\r
6830 UpdateICSWidth(hText);
\r
6831 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6834 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6842 if (hwndConsole) return;
\r
6843 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6844 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6849 ConsoleOutput(char* data, int length, int forceVisible)
\r
6854 char buf[CO_MAX+1];
\r
6857 static int delayLF = 0;
\r
6858 CHARRANGE savesel, sel;
\r
6860 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6868 while (length--) {
\r
6876 } else if (*p == '\007') {
\r
6877 MyPlaySound(&sounds[(int)SoundBell]);
\r
6884 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6885 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6886 /* Save current selection */
\r
6887 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6888 exlen = GetWindowTextLength(hText);
\r
6889 /* Find out whether current end of text is visible */
\r
6890 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6891 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6892 /* Trim existing text if it's too long */
\r
6893 if (exlen + (q - buf) > CO_MAX) {
\r
6894 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6897 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6898 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6900 savesel.cpMin -= trim;
\r
6901 savesel.cpMax -= trim;
\r
6902 if (exlen < 0) exlen = 0;
\r
6903 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6904 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6906 /* Append the new text */
\r
6907 sel.cpMin = exlen;
\r
6908 sel.cpMax = exlen;
\r
6909 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6910 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6911 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6912 if (forceVisible || exlen == 0 ||
\r
6913 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6914 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6915 /* Scroll to make new end of text visible if old end of text
\r
6916 was visible or new text is an echo of user typein */
\r
6917 sel.cpMin = 9999999;
\r
6918 sel.cpMax = 9999999;
\r
6919 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6920 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6921 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6922 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6924 if (savesel.cpMax == exlen || forceVisible) {
\r
6925 /* Move insert point to new end of text if it was at the old
\r
6926 end of text or if the new text is an echo of user typein */
\r
6927 sel.cpMin = 9999999;
\r
6928 sel.cpMax = 9999999;
\r
6929 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6931 /* Restore previous selection */
\r
6932 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6934 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6941 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6945 COLORREF oldFg, oldBg;
\r
6949 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6951 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6952 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6953 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6956 rect.right = x + squareSize;
\r
6958 rect.bottom = y + squareSize;
\r
6961 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6962 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6963 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6964 &rect, str, strlen(str), NULL);
\r
6966 (void) SetTextColor(hdc, oldFg);
\r
6967 (void) SetBkColor(hdc, oldBg);
\r
6968 (void) SelectObject(hdc, oldFont);
\r
6972 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6973 RECT *rect, char *color, char *flagFell)
\r
6977 COLORREF oldFg, oldBg;
\r
6980 if (appData.clockMode) {
\r
6982 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
6984 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
6991 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6992 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6994 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
6995 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
6997 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7001 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7002 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7003 rect, str, strlen(str), NULL);
\r
7004 if(logoHeight > 0 && appData.clockMode) {
\r
7006 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7007 r.top = rect->top + logoHeight/2;
\r
7008 r.left = rect->left;
\r
7009 r.right = rect->right;
\r
7010 r.bottom = rect->bottom;
\r
7011 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7012 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7013 &r, str, strlen(str), NULL);
\r
7015 (void) SetTextColor(hdc, oldFg);
\r
7016 (void) SetBkColor(hdc, oldBg);
\r
7017 (void) SelectObject(hdc, oldFont);
\r
7022 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7028 if( count <= 0 ) {
\r
7029 if (appData.debugMode) {
\r
7030 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7033 return ERROR_INVALID_USER_BUFFER;
\r
7036 ResetEvent(ovl->hEvent);
\r
7037 ovl->Offset = ovl->OffsetHigh = 0;
\r
7038 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7042 err = GetLastError();
\r
7043 if (err == ERROR_IO_PENDING) {
\r
7044 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7048 err = GetLastError();
\r
7055 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7060 ResetEvent(ovl->hEvent);
\r
7061 ovl->Offset = ovl->OffsetHigh = 0;
\r
7062 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7066 err = GetLastError();
\r
7067 if (err == ERROR_IO_PENDING) {
\r
7068 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7072 err = GetLastError();
\r
7078 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7079 void CheckForInputBufferFull( InputSource * is )
\r
7081 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7082 /* Look for end of line */
\r
7083 char * p = is->buf;
\r
7085 while( p < is->next && *p != '\n' ) {
\r
7089 if( p >= is->next ) {
\r
7090 if (appData.debugMode) {
\r
7091 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7094 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7095 is->count = (DWORD) -1;
\r
7096 is->next = is->buf;
\r
7102 InputThread(LPVOID arg)
\r
7107 is = (InputSource *) arg;
\r
7108 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7109 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7110 while (is->hThread != NULL) {
\r
7111 is->error = DoReadFile(is->hFile, is->next,
\r
7112 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7113 &is->count, &ovl);
\r
7114 if (is->error == NO_ERROR) {
\r
7115 is->next += is->count;
\r
7117 if (is->error == ERROR_BROKEN_PIPE) {
\r
7118 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7121 is->count = (DWORD) -1;
\r
7122 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7127 CheckForInputBufferFull( is );
\r
7129 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7131 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7133 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7136 CloseHandle(ovl.hEvent);
\r
7137 CloseHandle(is->hFile);
\r
7139 if (appData.debugMode) {
\r
7140 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7147 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7149 NonOvlInputThread(LPVOID arg)
\r
7156 is = (InputSource *) arg;
\r
7157 while (is->hThread != NULL) {
\r
7158 is->error = ReadFile(is->hFile, is->next,
\r
7159 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7160 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7161 if (is->error == NO_ERROR) {
\r
7162 /* Change CRLF to LF */
\r
7163 if (is->next > is->buf) {
\r
7165 i = is->count + 1;
\r
7173 if (prev == '\r' && *p == '\n') {
\r
7185 if (is->error == ERROR_BROKEN_PIPE) {
\r
7186 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7189 is->count = (DWORD) -1;
\r
7193 CheckForInputBufferFull( is );
\r
7195 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7197 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7199 if (is->count < 0) break; /* Quit on error */
\r
7201 CloseHandle(is->hFile);
\r
7206 SocketInputThread(LPVOID arg)
\r
7210 is = (InputSource *) arg;
\r
7211 while (is->hThread != NULL) {
\r
7212 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7213 if ((int)is->count == SOCKET_ERROR) {
\r
7214 is->count = (DWORD) -1;
\r
7215 is->error = WSAGetLastError();
\r
7217 is->error = NO_ERROR;
\r
7218 is->next += is->count;
\r
7219 if (is->count == 0 && is->second == is) {
\r
7220 /* End of file on stderr; quit with no message */
\r
7224 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7226 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7228 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7234 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7238 is = (InputSource *) lParam;
\r
7239 if (is->lineByLine) {
\r
7240 /* Feed in lines one by one */
\r
7241 char *p = is->buf;
\r
7243 while (q < is->next) {
\r
7244 if (*q++ == '\n') {
\r
7245 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7250 /* Move any partial line to the start of the buffer */
\r
7252 while (p < is->next) {
\r
7257 if (is->error != NO_ERROR || is->count == 0) {
\r
7258 /* Notify backend of the error. Note: If there was a partial
\r
7259 line at the end, it is not flushed through. */
\r
7260 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7263 /* Feed in the whole chunk of input at once */
\r
7264 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7265 is->next = is->buf;
\r
7269 /*---------------------------------------------------------------------------*\
\r
7271 * Menu enables. Used when setting various modes.
\r
7273 \*---------------------------------------------------------------------------*/
\r
7281 GreyRevert(Boolean grey)
\r
7282 { // [HGM] vari: for retracting variations in local mode
\r
7283 HMENU hmenu = GetMenu(hwndMain);
\r
7284 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7288 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7290 while (enab->item > 0) {
\r
7291 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7296 Enables gnuEnables[] = {
\r
7297 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7298 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7299 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7300 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7301 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7302 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7303 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7304 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7305 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7306 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7307 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7311 Enables icsEnables[] = {
\r
7312 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7313 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7314 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7315 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7316 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7317 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7318 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7319 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7320 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7321 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7322 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7323 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7324 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7325 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7326 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7331 Enables zippyEnables[] = {
\r
7332 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7333 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7334 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7335 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7340 Enables ncpEnables[] = {
\r
7341 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7342 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7343 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7344 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7345 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7346 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7347 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7348 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7349 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7350 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7351 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7352 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7353 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7354 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7355 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7356 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7357 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7358 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7359 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7363 Enables trainingOnEnables[] = {
\r
7364 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7365 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7366 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7367 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7368 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7369 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7370 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7371 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7375 Enables trainingOffEnables[] = {
\r
7376 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7377 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7378 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7379 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7380 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7381 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7382 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7383 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7387 /* These modify either ncpEnables or gnuEnables */
\r
7388 Enables cmailEnables[] = {
\r
7389 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7390 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7391 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7392 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7393 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7394 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7395 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7399 Enables machineThinkingEnables[] = {
\r
7400 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7401 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7402 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7403 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7404 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7405 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7406 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7407 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7408 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7409 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7410 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7411 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7412 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7413 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7418 Enables userThinkingEnables[] = {
\r
7419 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7420 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7421 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7422 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7423 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7424 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7425 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7426 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7427 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7428 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7429 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7430 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7431 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7432 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7433 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7437 /*---------------------------------------------------------------------------*\
\r
7439 * Front-end interface functions exported by XBoard.
\r
7440 * Functions appear in same order as prototypes in frontend.h.
\r
7442 \*---------------------------------------------------------------------------*/
\r
7446 static UINT prevChecked = 0;
\r
7447 static int prevPausing = 0;
\r
7450 if (pausing != prevPausing) {
\r
7451 prevPausing = pausing;
\r
7452 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7453 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7454 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7457 switch (gameMode) {
\r
7458 case BeginningOfGame:
\r
7459 if (appData.icsActive)
\r
7460 nowChecked = IDM_IcsClient;
\r
7461 else if (appData.noChessProgram)
\r
7462 nowChecked = IDM_EditGame;
\r
7464 nowChecked = IDM_MachineBlack;
\r
7466 case MachinePlaysBlack:
\r
7467 nowChecked = IDM_MachineBlack;
\r
7469 case MachinePlaysWhite:
\r
7470 nowChecked = IDM_MachineWhite;
\r
7472 case TwoMachinesPlay:
\r
7473 nowChecked = IDM_TwoMachines;
\r
7476 nowChecked = IDM_AnalysisMode;
\r
7479 nowChecked = IDM_AnalyzeFile;
\r
7482 nowChecked = IDM_EditGame;
\r
7484 case PlayFromGameFile:
\r
7485 nowChecked = IDM_LoadGame;
\r
7487 case EditPosition:
\r
7488 nowChecked = IDM_EditPosition;
\r
7491 nowChecked = IDM_Training;
\r
7493 case IcsPlayingWhite:
\r
7494 case IcsPlayingBlack:
\r
7495 case IcsObserving:
\r
7497 nowChecked = IDM_IcsClient;
\r
7504 if (prevChecked != 0)
\r
7505 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7506 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7507 if (nowChecked != 0)
\r
7508 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7509 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7511 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7512 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7513 MF_BYCOMMAND|MF_ENABLED);
\r
7515 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7516 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7519 prevChecked = nowChecked;
\r
7521 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7522 if (appData.icsActive) {
\r
7523 if (appData.icsEngineAnalyze) {
\r
7524 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7525 MF_BYCOMMAND|MF_CHECKED);
\r
7527 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7528 MF_BYCOMMAND|MF_UNCHECKED);
\r
7536 HMENU hmenu = GetMenu(hwndMain);
\r
7537 SetMenuEnables(hmenu, icsEnables);
\r
7538 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7539 MF_BYPOSITION|MF_ENABLED);
\r
7541 if (appData.zippyPlay) {
\r
7542 SetMenuEnables(hmenu, zippyEnables);
\r
7543 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7544 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7545 MF_BYCOMMAND|MF_ENABLED);
\r
7553 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7559 HMENU hmenu = GetMenu(hwndMain);
\r
7560 SetMenuEnables(hmenu, ncpEnables);
\r
7561 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7562 MF_BYPOSITION|MF_GRAYED);
\r
7563 DrawMenuBar(hwndMain);
\r
7569 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7573 SetTrainingModeOn()
\r
7576 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7577 for (i = 0; i < N_BUTTONS; i++) {
\r
7578 if (buttonDesc[i].hwnd != NULL)
\r
7579 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7584 VOID SetTrainingModeOff()
\r
7587 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7588 for (i = 0; i < N_BUTTONS; i++) {
\r
7589 if (buttonDesc[i].hwnd != NULL)
\r
7590 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7596 SetUserThinkingEnables()
\r
7598 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7602 SetMachineThinkingEnables()
\r
7604 HMENU hMenu = GetMenu(hwndMain);
\r
7605 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7607 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7609 if (gameMode == MachinePlaysBlack) {
\r
7610 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7611 } else if (gameMode == MachinePlaysWhite) {
\r
7612 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7613 } else if (gameMode == TwoMachinesPlay) {
\r
7614 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7620 DisplayTitle(char *str)
\r
7622 char title[MSG_SIZ], *host;
\r
7623 if (str[0] != NULLCHAR) {
\r
7624 strcpy(title, str);
\r
7625 } else if (appData.icsActive) {
\r
7626 if (appData.icsCommPort[0] != NULLCHAR)
\r
7629 host = appData.icsHost;
\r
7630 sprintf(title, "%s: %s", szTitle, host);
\r
7631 } else if (appData.noChessProgram) {
\r
7632 strcpy(title, szTitle);
\r
7634 strcpy(title, szTitle);
\r
7635 strcat(title, ": ");
\r
7636 strcat(title, first.tidy);
\r
7638 SetWindowText(hwndMain, title);
\r
7643 DisplayMessage(char *str1, char *str2)
\r
7647 int remain = MESSAGE_TEXT_MAX - 1;
\r
7650 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7651 messageText[0] = NULLCHAR;
\r
7653 len = strlen(str1);
\r
7654 if (len > remain) len = remain;
\r
7655 strncpy(messageText, str1, len);
\r
7656 messageText[len] = NULLCHAR;
\r
7659 if (*str2 && remain >= 2) {
\r
7661 strcat(messageText, " ");
\r
7664 len = strlen(str2);
\r
7665 if (len > remain) len = remain;
\r
7666 strncat(messageText, str2, len);
\r
7668 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7670 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7674 hdc = GetDC(hwndMain);
\r
7675 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7676 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7677 &messageRect, messageText, strlen(messageText), NULL);
\r
7678 (void) SelectObject(hdc, oldFont);
\r
7679 (void) ReleaseDC(hwndMain, hdc);
\r
7683 DisplayError(char *str, int error)
\r
7685 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7691 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7692 NULL, error, LANG_NEUTRAL,
\r
7693 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7695 sprintf(buf, "%s:\n%s", str, buf2);
\r
7697 ErrorMap *em = errmap;
\r
7698 while (em->err != 0 && em->err != error) em++;
\r
7699 if (em->err != 0) {
\r
7700 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7702 sprintf(buf, "%s:\nError code %d", str, error);
\r
7707 ErrorPopUp("Error", buf);
\r
7712 DisplayMoveError(char *str)
\r
7714 fromX = fromY = -1;
\r
7715 ClearHighlights();
\r
7716 DrawPosition(FALSE, NULL);
\r
7717 if (appData.popupMoveErrors) {
\r
7718 ErrorPopUp("Error", str);
\r
7720 DisplayMessage(str, "");
\r
7721 moveErrorMessageUp = TRUE;
\r
7726 DisplayFatalError(char *str, int error, int exitStatus)
\r
7728 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7730 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7733 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7734 NULL, error, LANG_NEUTRAL,
\r
7735 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7737 sprintf(buf, "%s:\n%s", str, buf2);
\r
7739 ErrorMap *em = errmap;
\r
7740 while (em->err != 0 && em->err != error) em++;
\r
7741 if (em->err != 0) {
\r
7742 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7744 sprintf(buf, "%s:\nError code %d", str, error);
\r
7749 if (appData.debugMode) {
\r
7750 fprintf(debugFP, "%s: %s\n", label, str);
\r
7752 if (appData.popupExitMessage) {
\r
7753 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7754 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7756 ExitEvent(exitStatus);
\r
7761 DisplayInformation(char *str)
\r
7763 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7768 DisplayNote(char *str)
\r
7770 ErrorPopUp("Note", str);
\r
7775 char *title, *question, *replyPrefix;
\r
7780 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7782 static QuestionParams *qp;
\r
7783 char reply[MSG_SIZ];
\r
7786 switch (message) {
\r
7787 case WM_INITDIALOG:
\r
7788 qp = (QuestionParams *) lParam;
\r
7789 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7790 SetWindowText(hDlg, qp->title);
\r
7791 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7792 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7796 switch (LOWORD(wParam)) {
\r
7798 strcpy(reply, qp->replyPrefix);
\r
7799 if (*reply) strcat(reply, " ");
\r
7800 len = strlen(reply);
\r
7801 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7802 strcat(reply, "\n");
\r
7803 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7804 EndDialog(hDlg, TRUE);
\r
7805 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7808 EndDialog(hDlg, FALSE);
\r
7819 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7821 QuestionParams qp;
\r
7825 qp.question = question;
\r
7826 qp.replyPrefix = replyPrefix;
\r
7828 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7829 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7830 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7831 FreeProcInstance(lpProc);
\r
7834 /* [AS] Pick FRC position */
\r
7835 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7837 static int * lpIndexFRC;
\r
7843 case WM_INITDIALOG:
\r
7844 lpIndexFRC = (int *) lParam;
\r
7846 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7848 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7849 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7850 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7851 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7856 switch( LOWORD(wParam) ) {
\r
7858 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7859 EndDialog( hDlg, 0 );
\r
7860 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7863 EndDialog( hDlg, 1 );
\r
7865 case IDC_NFG_Edit:
\r
7866 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7867 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7869 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7872 case IDC_NFG_Random:
\r
7873 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7874 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7887 int index = appData.defaultFrcPosition;
\r
7888 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7890 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7892 if( result == 0 ) {
\r
7893 appData.defaultFrcPosition = index;
\r
7899 /* [AS] Game list options. Refactored by HGM */
\r
7901 HWND gameListOptionsDialog;
\r
7903 // low-level front-end: clear text edit / list widget
\r
7907 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7910 // low-level front-end: clear text edit / list widget
\r
7912 GLT_DeSelectList()
\r
7914 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7917 // low-level front-end: append line to text edit / list widget
\r
7919 GLT_AddToList( char *name )
\r
7922 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
7926 // low-level front-end: get line from text edit / list widget
\r
7928 GLT_GetFromList( int index, char *name )
\r
7931 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
7937 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7939 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7940 int idx2 = idx1 + delta;
\r
7941 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7943 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7946 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7947 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7948 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7949 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7953 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7957 case WM_INITDIALOG:
\r
7958 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
7960 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7962 /* Initialize list */
\r
7963 GLT_TagsToList( lpUserGLT );
\r
7965 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
7970 switch( LOWORD(wParam) ) {
\r
7973 EndDialog( hDlg, 0 );
\r
7976 EndDialog( hDlg, 1 );
\r
7979 case IDC_GLT_Default:
\r
7980 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
7983 case IDC_GLT_Restore:
\r
7984 GLT_TagsToList( appData.gameListTags );
\r
7988 GLT_MoveSelection( hDlg, -1 );
\r
7991 case IDC_GLT_Down:
\r
7992 GLT_MoveSelection( hDlg, +1 );
\r
8002 int GameListOptions()
\r
8005 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8007 strcpy( lpUserGLT, appData.gameListTags );
\r
8009 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8011 if( result == 0 ) {
\r
8012 /* [AS] Memory leak here! */
\r
8013 appData.gameListTags = strdup( lpUserGLT );
\r
8020 DisplayIcsInteractionTitle(char *str)
\r
8022 char consoleTitle[MSG_SIZ];
\r
8024 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8025 SetWindowText(hwndConsole, consoleTitle);
\r
8029 DrawPosition(int fullRedraw, Board board)
\r
8031 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8034 void NotifyFrontendLogin()
\r
8037 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8043 fromX = fromY = -1;
\r
8044 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8045 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8046 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8047 dragInfo.lastpos = dragInfo.pos;
\r
8048 dragInfo.start.x = dragInfo.start.y = -1;
\r
8049 dragInfo.from = dragInfo.start;
\r
8051 DrawPosition(TRUE, NULL);
\r
8057 CommentPopUp(char *title, char *str)
\r
8059 HWND hwnd = GetActiveWindow();
\r
8060 EitherCommentPopUp(0, title, str, FALSE);
\r
8062 SetActiveWindow(hwnd);
\r
8066 CommentPopDown(void)
\r
8068 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8069 if (commentDialog) {
\r
8070 ShowWindow(commentDialog, SW_HIDE);
\r
8072 commentUp = FALSE;
\r
8076 EditCommentPopUp(int index, char *title, char *str)
\r
8078 EitherCommentPopUp(index, title, str, TRUE);
\r
8085 MyPlaySound(&sounds[(int)SoundMove]);
\r
8088 VOID PlayIcsWinSound()
\r
8090 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8093 VOID PlayIcsLossSound()
\r
8095 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8098 VOID PlayIcsDrawSound()
\r
8100 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8103 VOID PlayIcsUnfinishedSound()
\r
8105 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8111 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8119 consoleEcho = TRUE;
\r
8120 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8121 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8122 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8131 consoleEcho = FALSE;
\r
8132 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8133 /* This works OK: set text and background both to the same color */
\r
8135 cf.crTextColor = COLOR_ECHOOFF;
\r
8136 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8137 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8140 /* No Raw()...? */
\r
8142 void Colorize(ColorClass cc, int continuation)
\r
8144 currentColorClass = cc;
\r
8145 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8146 consoleCF.crTextColor = textAttribs[cc].color;
\r
8147 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8148 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8154 static char buf[MSG_SIZ];
\r
8155 DWORD bufsiz = MSG_SIZ;
\r
8157 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8158 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8160 if (!GetUserName(buf, &bufsiz)) {
\r
8161 /*DisplayError("Error getting user name", GetLastError());*/
\r
8162 strcpy(buf, "User");
\r
8170 static char buf[MSG_SIZ];
\r
8171 DWORD bufsiz = MSG_SIZ;
\r
8173 if (!GetComputerName(buf, &bufsiz)) {
\r
8174 /*DisplayError("Error getting host name", GetLastError());*/
\r
8175 strcpy(buf, "Unknown");
\r
8182 ClockTimerRunning()
\r
8184 return clockTimerEvent != 0;
\r
8190 if (clockTimerEvent == 0) return FALSE;
\r
8191 KillTimer(hwndMain, clockTimerEvent);
\r
8192 clockTimerEvent = 0;
\r
8197 StartClockTimer(long millisec)
\r
8199 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8200 (UINT) millisec, NULL);
\r
8204 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8207 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8209 if(appData.noGUI) return;
\r
8210 hdc = GetDC(hwndMain);
\r
8211 if (!IsIconic(hwndMain)) {
\r
8212 DisplayAClock(hdc, timeRemaining, highlight,
\r
8213 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8215 if (highlight && iconCurrent == iconBlack) {
\r
8216 iconCurrent = iconWhite;
\r
8217 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8218 if (IsIconic(hwndMain)) {
\r
8219 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8222 (void) ReleaseDC(hwndMain, hdc);
\r
8224 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8228 DisplayBlackClock(long timeRemaining, int highlight)
\r
8231 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8233 if(appData.noGUI) return;
\r
8234 hdc = GetDC(hwndMain);
\r
8235 if (!IsIconic(hwndMain)) {
\r
8236 DisplayAClock(hdc, timeRemaining, highlight,
\r
8237 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8239 if (highlight && iconCurrent == iconWhite) {
\r
8240 iconCurrent = iconBlack;
\r
8241 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8242 if (IsIconic(hwndMain)) {
\r
8243 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8246 (void) ReleaseDC(hwndMain, hdc);
\r
8248 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8253 LoadGameTimerRunning()
\r
8255 return loadGameTimerEvent != 0;
\r
8259 StopLoadGameTimer()
\r
8261 if (loadGameTimerEvent == 0) return FALSE;
\r
8262 KillTimer(hwndMain, loadGameTimerEvent);
\r
8263 loadGameTimerEvent = 0;
\r
8268 StartLoadGameTimer(long millisec)
\r
8270 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8271 (UINT) millisec, NULL);
\r
8279 char fileTitle[MSG_SIZ];
\r
8281 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8282 f = OpenFileDialog(hwndMain, "a", defName,
\r
8283 appData.oldSaveStyle ? "gam" : "pgn",
\r
8285 "Save Game to File", NULL, fileTitle, NULL);
\r
8287 SaveGame(f, 0, "");
\r
8294 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8296 if (delayedTimerEvent != 0) {
\r
8297 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8298 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8300 KillTimer(hwndMain, delayedTimerEvent);
\r
8301 delayedTimerEvent = 0;
\r
8302 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8303 delayedTimerCallback();
\r
8305 delayedTimerCallback = cb;
\r
8306 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8307 (UINT) millisec, NULL);
\r
8310 DelayedEventCallback
\r
8313 if (delayedTimerEvent) {
\r
8314 return delayedTimerCallback;
\r
8321 CancelDelayedEvent()
\r
8323 if (delayedTimerEvent) {
\r
8324 KillTimer(hwndMain, delayedTimerEvent);
\r
8325 delayedTimerEvent = 0;
\r
8329 DWORD GetWin32Priority(int nice)
\r
8330 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8332 REALTIME_PRIORITY_CLASS 0x00000100
\r
8333 HIGH_PRIORITY_CLASS 0x00000080
\r
8334 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8335 NORMAL_PRIORITY_CLASS 0x00000020
\r
8336 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8337 IDLE_PRIORITY_CLASS 0x00000040
\r
8339 if (nice < -15) return 0x00000080;
\r
8340 if (nice < 0) return 0x00008000;
\r
8341 if (nice == 0) return 0x00000020;
\r
8342 if (nice < 15) return 0x00004000;
\r
8343 return 0x00000040;
\r
8346 /* Start a child process running the given program.
\r
8347 The process's standard output can be read from "from", and its
\r
8348 standard input can be written to "to".
\r
8349 Exit with fatal error if anything goes wrong.
\r
8350 Returns an opaque pointer that can be used to destroy the process
\r
8354 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8356 #define BUFSIZE 4096
\r
8358 HANDLE hChildStdinRd, hChildStdinWr,
\r
8359 hChildStdoutRd, hChildStdoutWr;
\r
8360 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8361 SECURITY_ATTRIBUTES saAttr;
\r
8363 PROCESS_INFORMATION piProcInfo;
\r
8364 STARTUPINFO siStartInfo;
\r
8366 char buf[MSG_SIZ];
\r
8369 if (appData.debugMode) {
\r
8370 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8375 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8376 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8377 saAttr.bInheritHandle = TRUE;
\r
8378 saAttr.lpSecurityDescriptor = NULL;
\r
8381 * The steps for redirecting child's STDOUT:
\r
8382 * 1. Create anonymous pipe to be STDOUT for child.
\r
8383 * 2. Create a noninheritable duplicate of read handle,
\r
8384 * and close the inheritable read handle.
\r
8387 /* Create a pipe for the child's STDOUT. */
\r
8388 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8389 return GetLastError();
\r
8392 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8393 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8394 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8395 FALSE, /* not inherited */
\r
8396 DUPLICATE_SAME_ACCESS);
\r
8398 return GetLastError();
\r
8400 CloseHandle(hChildStdoutRd);
\r
8403 * The steps for redirecting child's STDIN:
\r
8404 * 1. Create anonymous pipe to be STDIN for child.
\r
8405 * 2. Create a noninheritable duplicate of write handle,
\r
8406 * and close the inheritable write handle.
\r
8409 /* Create a pipe for the child's STDIN. */
\r
8410 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8411 return GetLastError();
\r
8414 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8415 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8416 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8417 FALSE, /* not inherited */
\r
8418 DUPLICATE_SAME_ACCESS);
\r
8420 return GetLastError();
\r
8422 CloseHandle(hChildStdinWr);
\r
8424 /* Arrange to (1) look in dir for the child .exe file, and
\r
8425 * (2) have dir be the child's working directory. Interpret
\r
8426 * dir relative to the directory WinBoard loaded from. */
\r
8427 GetCurrentDirectory(MSG_SIZ, buf);
\r
8428 SetCurrentDirectory(installDir);
\r
8429 SetCurrentDirectory(dir);
\r
8431 /* Now create the child process. */
\r
8433 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8434 siStartInfo.lpReserved = NULL;
\r
8435 siStartInfo.lpDesktop = NULL;
\r
8436 siStartInfo.lpTitle = NULL;
\r
8437 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8438 siStartInfo.cbReserved2 = 0;
\r
8439 siStartInfo.lpReserved2 = NULL;
\r
8440 siStartInfo.hStdInput = hChildStdinRd;
\r
8441 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8442 siStartInfo.hStdError = hChildStdoutWr;
\r
8444 fSuccess = CreateProcess(NULL,
\r
8445 cmdLine, /* command line */
\r
8446 NULL, /* process security attributes */
\r
8447 NULL, /* primary thread security attrs */
\r
8448 TRUE, /* handles are inherited */
\r
8449 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8450 NULL, /* use parent's environment */
\r
8452 &siStartInfo, /* STARTUPINFO pointer */
\r
8453 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8455 err = GetLastError();
\r
8456 SetCurrentDirectory(buf); /* return to prev directory */
\r
8461 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8462 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8463 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8466 /* Close the handles we don't need in the parent */
\r
8467 CloseHandle(piProcInfo.hThread);
\r
8468 CloseHandle(hChildStdinRd);
\r
8469 CloseHandle(hChildStdoutWr);
\r
8471 /* Prepare return value */
\r
8472 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8473 cp->kind = CPReal;
\r
8474 cp->hProcess = piProcInfo.hProcess;
\r
8475 cp->pid = piProcInfo.dwProcessId;
\r
8476 cp->hFrom = hChildStdoutRdDup;
\r
8477 cp->hTo = hChildStdinWrDup;
\r
8479 *pr = (void *) cp;
\r
8481 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8482 2000 where engines sometimes don't see the initial command(s)
\r
8483 from WinBoard and hang. I don't understand how that can happen,
\r
8484 but the Sleep is harmless, so I've put it in. Others have also
\r
8485 reported what may be the same problem, so hopefully this will fix
\r
8486 it for them too. */
\r
8494 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8496 ChildProc *cp; int result;
\r
8498 cp = (ChildProc *) pr;
\r
8499 if (cp == NULL) return;
\r
8501 switch (cp->kind) {
\r
8503 /* TerminateProcess is considered harmful, so... */
\r
8504 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8505 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8506 /* The following doesn't work because the chess program
\r
8507 doesn't "have the same console" as WinBoard. Maybe
\r
8508 we could arrange for this even though neither WinBoard
\r
8509 nor the chess program uses a console for stdio? */
\r
8510 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8512 /* [AS] Special termination modes for misbehaving programs... */
\r
8513 if( signal == 9 ) {
\r
8514 result = TerminateProcess( cp->hProcess, 0 );
\r
8516 if ( appData.debugMode) {
\r
8517 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8520 else if( signal == 10 ) {
\r
8521 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8523 if( dw != WAIT_OBJECT_0 ) {
\r
8524 result = TerminateProcess( cp->hProcess, 0 );
\r
8526 if ( appData.debugMode) {
\r
8527 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8533 CloseHandle(cp->hProcess);
\r
8537 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8541 closesocket(cp->sock);
\r
8546 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8547 closesocket(cp->sock);
\r
8548 closesocket(cp->sock2);
\r
8556 InterruptChildProcess(ProcRef pr)
\r
8560 cp = (ChildProc *) pr;
\r
8561 if (cp == NULL) return;
\r
8562 switch (cp->kind) {
\r
8564 /* The following doesn't work because the chess program
\r
8565 doesn't "have the same console" as WinBoard. Maybe
\r
8566 we could arrange for this even though neither WinBoard
\r
8567 nor the chess program uses a console for stdio */
\r
8568 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8573 /* Can't interrupt */
\r
8577 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8584 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8586 char cmdLine[MSG_SIZ];
\r
8588 if (port[0] == NULLCHAR) {
\r
8589 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8591 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8593 return StartChildProcess(cmdLine, "", pr);
\r
8597 /* Code to open TCP sockets */
\r
8600 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8605 struct sockaddr_in sa, mysa;
\r
8606 struct hostent FAR *hp;
\r
8607 unsigned short uport;
\r
8608 WORD wVersionRequested;
\r
8611 /* Initialize socket DLL */
\r
8612 wVersionRequested = MAKEWORD(1, 1);
\r
8613 err = WSAStartup(wVersionRequested, &wsaData);
\r
8614 if (err != 0) return err;
\r
8617 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8618 err = WSAGetLastError();
\r
8623 /* Bind local address using (mostly) don't-care values.
\r
8625 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8626 mysa.sin_family = AF_INET;
\r
8627 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8628 uport = (unsigned short) 0;
\r
8629 mysa.sin_port = htons(uport);
\r
8630 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8631 == SOCKET_ERROR) {
\r
8632 err = WSAGetLastError();
\r
8637 /* Resolve remote host name */
\r
8638 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8639 if (!(hp = gethostbyname(host))) {
\r
8640 unsigned int b0, b1, b2, b3;
\r
8642 err = WSAGetLastError();
\r
8644 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8645 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8646 hp->h_addrtype = AF_INET;
\r
8648 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8649 hp->h_addr_list[0] = (char *) malloc(4);
\r
8650 hp->h_addr_list[0][0] = (char) b0;
\r
8651 hp->h_addr_list[0][1] = (char) b1;
\r
8652 hp->h_addr_list[0][2] = (char) b2;
\r
8653 hp->h_addr_list[0][3] = (char) b3;
\r
8659 sa.sin_family = hp->h_addrtype;
\r
8660 uport = (unsigned short) atoi(port);
\r
8661 sa.sin_port = htons(uport);
\r
8662 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8664 /* Make connection */
\r
8665 if (connect(s, (struct sockaddr *) &sa,
\r
8666 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8667 err = WSAGetLastError();
\r
8672 /* Prepare return value */
\r
8673 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8674 cp->kind = CPSock;
\r
8676 *pr = (ProcRef *) cp;
\r
8682 OpenCommPort(char *name, ProcRef *pr)
\r
8687 char fullname[MSG_SIZ];
\r
8689 if (*name != '\\')
\r
8690 sprintf(fullname, "\\\\.\\%s", name);
\r
8692 strcpy(fullname, name);
\r
8694 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8695 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8696 if (h == (HANDLE) -1) {
\r
8697 return GetLastError();
\r
8701 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8703 /* Accumulate characters until a 100ms pause, then parse */
\r
8704 ct.ReadIntervalTimeout = 100;
\r
8705 ct.ReadTotalTimeoutMultiplier = 0;
\r
8706 ct.ReadTotalTimeoutConstant = 0;
\r
8707 ct.WriteTotalTimeoutMultiplier = 0;
\r
8708 ct.WriteTotalTimeoutConstant = 0;
\r
8709 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8711 /* Prepare return value */
\r
8712 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8713 cp->kind = CPComm;
\r
8716 *pr = (ProcRef *) cp;
\r
8722 OpenLoopback(ProcRef *pr)
\r
8724 DisplayFatalError("Not implemented", 0, 1);
\r
8730 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8735 struct sockaddr_in sa, mysa;
\r
8736 struct hostent FAR *hp;
\r
8737 unsigned short uport;
\r
8738 WORD wVersionRequested;
\r
8741 char stderrPortStr[MSG_SIZ];
\r
8743 /* Initialize socket DLL */
\r
8744 wVersionRequested = MAKEWORD(1, 1);
\r
8745 err = WSAStartup(wVersionRequested, &wsaData);
\r
8746 if (err != 0) return err;
\r
8748 /* Resolve remote host name */
\r
8749 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8750 if (!(hp = gethostbyname(host))) {
\r
8751 unsigned int b0, b1, b2, b3;
\r
8753 err = WSAGetLastError();
\r
8755 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8756 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8757 hp->h_addrtype = AF_INET;
\r
8759 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8760 hp->h_addr_list[0] = (char *) malloc(4);
\r
8761 hp->h_addr_list[0][0] = (char) b0;
\r
8762 hp->h_addr_list[0][1] = (char) b1;
\r
8763 hp->h_addr_list[0][2] = (char) b2;
\r
8764 hp->h_addr_list[0][3] = (char) b3;
\r
8770 sa.sin_family = hp->h_addrtype;
\r
8771 uport = (unsigned short) 514;
\r
8772 sa.sin_port = htons(uport);
\r
8773 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8775 /* Bind local socket to unused "privileged" port address
\r
8777 s = INVALID_SOCKET;
\r
8778 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8779 mysa.sin_family = AF_INET;
\r
8780 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8781 for (fromPort = 1023;; fromPort--) {
\r
8782 if (fromPort < 0) {
\r
8784 return WSAEADDRINUSE;
\r
8786 if (s == INVALID_SOCKET) {
\r
8787 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8788 err = WSAGetLastError();
\r
8793 uport = (unsigned short) fromPort;
\r
8794 mysa.sin_port = htons(uport);
\r
8795 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8796 == SOCKET_ERROR) {
\r
8797 err = WSAGetLastError();
\r
8798 if (err == WSAEADDRINUSE) continue;
\r
8802 if (connect(s, (struct sockaddr *) &sa,
\r
8803 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8804 err = WSAGetLastError();
\r
8805 if (err == WSAEADDRINUSE) {
\r
8816 /* Bind stderr local socket to unused "privileged" port address
\r
8818 s2 = INVALID_SOCKET;
\r
8819 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8820 mysa.sin_family = AF_INET;
\r
8821 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8822 for (fromPort = 1023;; fromPort--) {
\r
8823 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8824 if (fromPort < 0) {
\r
8825 (void) closesocket(s);
\r
8827 return WSAEADDRINUSE;
\r
8829 if (s2 == INVALID_SOCKET) {
\r
8830 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8831 err = WSAGetLastError();
\r
8837 uport = (unsigned short) fromPort;
\r
8838 mysa.sin_port = htons(uport);
\r
8839 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8840 == SOCKET_ERROR) {
\r
8841 err = WSAGetLastError();
\r
8842 if (err == WSAEADDRINUSE) continue;
\r
8843 (void) closesocket(s);
\r
8847 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8848 err = WSAGetLastError();
\r
8849 if (err == WSAEADDRINUSE) {
\r
8851 s2 = INVALID_SOCKET;
\r
8854 (void) closesocket(s);
\r
8855 (void) closesocket(s2);
\r
8861 prevStderrPort = fromPort; // remember port used
\r
8862 sprintf(stderrPortStr, "%d", fromPort);
\r
8864 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8865 err = WSAGetLastError();
\r
8866 (void) closesocket(s);
\r
8867 (void) closesocket(s2);
\r
8872 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8873 err = WSAGetLastError();
\r
8874 (void) closesocket(s);
\r
8875 (void) closesocket(s2);
\r
8879 if (*user == NULLCHAR) user = UserName();
\r
8880 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8881 err = WSAGetLastError();
\r
8882 (void) closesocket(s);
\r
8883 (void) closesocket(s2);
\r
8887 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8888 err = WSAGetLastError();
\r
8889 (void) closesocket(s);
\r
8890 (void) closesocket(s2);
\r
8895 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8896 err = WSAGetLastError();
\r
8897 (void) closesocket(s);
\r
8898 (void) closesocket(s2);
\r
8902 (void) closesocket(s2); /* Stop listening */
\r
8904 /* Prepare return value */
\r
8905 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8906 cp->kind = CPRcmd;
\r
8909 *pr = (ProcRef *) cp;
\r
8916 AddInputSource(ProcRef pr, int lineByLine,
\r
8917 InputCallback func, VOIDSTAR closure)
\r
8919 InputSource *is, *is2 = NULL;
\r
8920 ChildProc *cp = (ChildProc *) pr;
\r
8922 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8923 is->lineByLine = lineByLine;
\r
8925 is->closure = closure;
\r
8926 is->second = NULL;
\r
8927 is->next = is->buf;
\r
8928 if (pr == NoProc) {
\r
8929 is->kind = CPReal;
\r
8930 consoleInputSource = is;
\r
8932 is->kind = cp->kind;
\r
8934 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8935 we create all threads suspended so that the is->hThread variable can be
\r
8936 safely assigned, then let the threads start with ResumeThread.
\r
8938 switch (cp->kind) {
\r
8940 is->hFile = cp->hFrom;
\r
8941 cp->hFrom = NULL; /* now owned by InputThread */
\r
8943 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8944 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8948 is->hFile = cp->hFrom;
\r
8949 cp->hFrom = NULL; /* now owned by InputThread */
\r
8951 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
8952 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8956 is->sock = cp->sock;
\r
8958 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8959 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8963 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
8965 is->sock = cp->sock;
\r
8967 is2->sock = cp->sock2;
\r
8968 is2->second = is2;
\r
8970 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8971 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8973 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8974 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
8978 if( is->hThread != NULL ) {
\r
8979 ResumeThread( is->hThread );
\r
8982 if( is2 != NULL && is2->hThread != NULL ) {
\r
8983 ResumeThread( is2->hThread );
\r
8987 return (InputSourceRef) is;
\r
8991 RemoveInputSource(InputSourceRef isr)
\r
8995 is = (InputSource *) isr;
\r
8996 is->hThread = NULL; /* tell thread to stop */
\r
8997 CloseHandle(is->hThread);
\r
8998 if (is->second != NULL) {
\r
8999 is->second->hThread = NULL;
\r
9000 CloseHandle(is->second->hThread);
\r
9004 int no_wrap(char *message, int count)
\r
9006 ConsoleOutput(message, count, FALSE);
\r
9011 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9014 int outCount = SOCKET_ERROR;
\r
9015 ChildProc *cp = (ChildProc *) pr;
\r
9016 static OVERLAPPED ovl;
\r
9017 static int line = 0;
\r
9021 if (appData.noJoin || !appData.useInternalWrap)
\r
9022 return no_wrap(message, count);
\r
9025 int width = get_term_width();
\r
9026 int len = wrap(NULL, message, count, width, &line);
\r
9027 char *msg = malloc(len);
\r
9031 return no_wrap(message, count);
\r
9034 dbgchk = wrap(msg, message, count, width, &line);
\r
9035 if (dbgchk != len && appData.debugMode)
\r
9036 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9037 ConsoleOutput(msg, len, FALSE);
\r
9044 if (ovl.hEvent == NULL) {
\r
9045 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9047 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9049 switch (cp->kind) {
\r
9052 outCount = send(cp->sock, message, count, 0);
\r
9053 if (outCount == SOCKET_ERROR) {
\r
9054 *outError = WSAGetLastError();
\r
9056 *outError = NO_ERROR;
\r
9061 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9062 &dOutCount, NULL)) {
\r
9063 *outError = NO_ERROR;
\r
9064 outCount = (int) dOutCount;
\r
9066 *outError = GetLastError();
\r
9071 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9072 &dOutCount, &ovl);
\r
9073 if (*outError == NO_ERROR) {
\r
9074 outCount = (int) dOutCount;
\r
9082 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9085 /* Ignore delay, not implemented for WinBoard */
\r
9086 return OutputToProcess(pr, message, count, outError);
\r
9091 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9092 char *buf, int count, int error)
\r
9094 DisplayFatalError("Not implemented", 0, 1);
\r
9097 /* see wgamelist.c for Game List functions */
\r
9098 /* see wedittags.c for Edit Tags functions */
\r
9105 char buf[MSG_SIZ];
\r
9108 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9109 f = fopen(buf, "r");
\r
9111 ProcessICSInitScript(f);
\r
9119 StartAnalysisClock()
\r
9121 if (analysisTimerEvent) return;
\r
9122 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9123 (UINT) 2000, NULL);
\r
9127 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9129 highlightInfo.sq[0].x = fromX;
\r
9130 highlightInfo.sq[0].y = fromY;
\r
9131 highlightInfo.sq[1].x = toX;
\r
9132 highlightInfo.sq[1].y = toY;
\r
9138 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9139 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9143 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9145 premoveHighlightInfo.sq[0].x = fromX;
\r
9146 premoveHighlightInfo.sq[0].y = fromY;
\r
9147 premoveHighlightInfo.sq[1].x = toX;
\r
9148 premoveHighlightInfo.sq[1].y = toY;
\r
9152 ClearPremoveHighlights()
\r
9154 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9155 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9159 ShutDownFrontEnd()
\r
9161 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9162 DeleteClipboardTempFiles();
\r
9168 if (IsIconic(hwndMain))
\r
9169 ShowWindow(hwndMain, SW_RESTORE);
\r
9171 SetActiveWindow(hwndMain);
\r
9175 * Prototypes for animation support routines
\r
9177 static void ScreenSquare(int column, int row, POINT * pt);
\r
9178 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9179 POINT frames[], int * nFrames);
\r
9183 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9184 { // [HGM] atomic: animate blast wave
\r
9186 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9187 explodeInfo.fromX = fromX;
\r
9188 explodeInfo.fromY = fromY;
\r
9189 explodeInfo.toX = toX;
\r
9190 explodeInfo.toY = toY;
\r
9191 for(i=1; i<nFrames; i++) {
\r
9192 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9193 DrawPosition(FALSE, NULL);
\r
9194 Sleep(appData.animSpeed);
\r
9196 explodeInfo.radius = 0;
\r
9197 DrawPosition(TRUE, NULL);
\r
9203 AnimateMove(board, fromX, fromY, toX, toY)
\r
9210 ChessSquare piece;
\r
9211 POINT start, finish, mid;
\r
9212 POINT frames[kFactor * 2 + 1];
\r
9215 if (!appData.animate) return;
\r
9216 if (doingSizing) return;
\r
9217 if (fromY < 0 || fromX < 0) return;
\r
9218 piece = board[fromY][fromX];
\r
9219 if (piece >= EmptySquare) return;
\r
9221 ScreenSquare(fromX, fromY, &start);
\r
9222 ScreenSquare(toX, toY, &finish);
\r
9224 /* All pieces except knights move in straight line */
\r
9225 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9226 mid.x = start.x + (finish.x - start.x) / 2;
\r
9227 mid.y = start.y + (finish.y - start.y) / 2;
\r
9229 /* Knight: make diagonal movement then straight */
\r
9230 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9231 mid.x = start.x + (finish.x - start.x) / 2;
\r
9235 mid.y = start.y + (finish.y - start.y) / 2;
\r
9239 /* Don't use as many frames for very short moves */
\r
9240 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9241 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9243 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9245 animInfo.from.x = fromX;
\r
9246 animInfo.from.y = fromY;
\r
9247 animInfo.to.x = toX;
\r
9248 animInfo.to.y = toY;
\r
9249 animInfo.lastpos = start;
\r
9250 animInfo.piece = piece;
\r
9251 for (n = 0; n < nFrames; n++) {
\r
9252 animInfo.pos = frames[n];
\r
9253 DrawPosition(FALSE, NULL);
\r
9254 animInfo.lastpos = animInfo.pos;
\r
9255 Sleep(appData.animSpeed);
\r
9257 animInfo.pos = finish;
\r
9258 DrawPosition(FALSE, NULL);
\r
9259 animInfo.piece = EmptySquare;
\r
9260 if(gameInfo.variant == VariantAtomic &&
\r
9261 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9262 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9265 /* Convert board position to corner of screen rect and color */
\r
9268 ScreenSquare(column, row, pt)
\r
9269 int column; int row; POINT * pt;
\r
9272 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9273 pt->y = lineGap + row * (squareSize + lineGap);
\r
9275 pt->x = lineGap + column * (squareSize + lineGap);
\r
9276 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9280 /* Generate a series of frame coords from start->mid->finish.
\r
9281 The movement rate doubles until the half way point is
\r
9282 reached, then halves back down to the final destination,
\r
9283 which gives a nice slow in/out effect. The algorithmn
\r
9284 may seem to generate too many intermediates for short
\r
9285 moves, but remember that the purpose is to attract the
\r
9286 viewers attention to the piece about to be moved and
\r
9287 then to where it ends up. Too few frames would be less
\r
9291 Tween(start, mid, finish, factor, frames, nFrames)
\r
9292 POINT * start; POINT * mid;
\r
9293 POINT * finish; int factor;
\r
9294 POINT frames[]; int * nFrames;
\r
9296 int n, fraction = 1, count = 0;
\r
9298 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9299 for (n = 0; n < factor; n++)
\r
9301 for (n = 0; n < factor; n++) {
\r
9302 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9303 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9305 fraction = fraction / 2;
\r
9309 frames[count] = *mid;
\r
9312 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9314 for (n = 0; n < factor; n++) {
\r
9315 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9316 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9318 fraction = fraction * 2;
\r
9324 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9326 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9328 EvalGraphSet( first, last, current, pvInfoList );
\r