2 * WinBoard.c -- Windows NT front end to XBoard
\r
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
\r
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
\r
8 * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
\r
10 * Enhancements Copyright 2005 Alessandro Scotti
\r
12 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
13 * which was written and is copyrighted by Wayne Christopher.
\r
15 * The following terms apply to Digital Equipment Corporation's copyright
\r
16 * interest in XBoard:
\r
17 * ------------------------------------------------------------------------
\r
18 * All Rights Reserved
\r
20 * Permission to use, copy, modify, and distribute this software and its
\r
21 * documentation for any purpose and without fee is hereby granted,
\r
22 * provided that the above copyright notice appear in all copies and that
\r
23 * both that copyright notice and this permission notice appear in
\r
24 * supporting documentation, and that the name of Digital not be
\r
25 * used in advertising or publicity pertaining to distribution of the
\r
26 * software without specific, written prior permission.
\r
28 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
29 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
30 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
31 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
32 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
33 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
35 * ------------------------------------------------------------------------
\r
37 * The following terms apply to the enhanced version of XBoard
\r
38 * distributed by the Free Software Foundation:
\r
39 * ------------------------------------------------------------------------
\r
41 * GNU XBoard is free software: you can redistribute it and/or modify
\r
42 * it under the terms of the GNU General Public License as published by
\r
43 * the Free Software Foundation, either version 3 of the License, or (at
\r
44 * your option) any later version.
\r
46 * GNU XBoard is distributed in the hope that it will be useful, but
\r
47 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
49 * General Public License for more details.
\r
51 * You should have received a copy of the GNU General Public License
\r
52 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
54 *------------------------------------------------------------------------
\r
55 ** See the file ChangeLog for a revision history. */
\r
59 #include <windows.h>
\r
60 #include <winuser.h>
\r
61 #include <winsock.h>
\r
62 #include <commctrl.h>
\r
68 #include <sys/stat.h>
\r
71 #include <commdlg.h>
\r
73 #include <richedit.h>
\r
74 #include <mmsystem.h>
\r
83 #include "frontend.h"
\r
84 #include "backend.h"
\r
85 #include "winboard.h"
\r
87 #include "wclipbrd.h"
\r
88 #include "woptions.h"
\r
89 #include "wsockerr.h"
\r
90 #include "defaults.h"
\r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );
\r
97 void mysrandom(unsigned int seed);
\r
99 extern int whiteFlag, blackFlag;
\r
100 Boolean flipClock = FALSE;
\r
101 extern HANDLE chatHandle[];
\r
102 extern int ics_type;
\r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);
\r
105 VOID NewVariantPopup(HWND hwnd);
\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
\r
107 /*char*/int promoChar));
\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);
\r
109 void DisplayMove P((int moveNumber));
\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
\r
111 void ChatPopUp P((char *s));
\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
862 if(appData.chatBoxes) { // [HGM] chat: open chat boxes
\r
863 char buf[MSG_SIZ], *p = buf, *q;
\r
864 strcpy(buf, appData.chatBoxes);
\r
866 q = strchr(p, ';');
\r
868 if(*p) ChatPopUp(p);
\r
871 SetActiveWindow(hwndConsole);
\r
873 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
874 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
883 HMENU hmenu = GetMenu(hwndMain);
\r
885 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
886 MF_BYCOMMAND|((appData.icsActive &&
\r
887 *appData.icsCommPort != NULLCHAR) ?
\r
888 MF_ENABLED : MF_GRAYED));
\r
889 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
890 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
891 MF_CHECKED : MF_UNCHECKED));
\r
894 //---------------------------------------------------------------------------------------------------------
\r
896 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
897 #define XBOARD FALSE
\r
899 #define OPTCHAR "/"
\r
900 #define SEPCHAR "="
\r
904 // front-end part of option handling
\r
907 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
909 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
910 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
913 lf->lfEscapement = 0;
\r
914 lf->lfOrientation = 0;
\r
915 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
916 lf->lfItalic = mfp->italic;
\r
917 lf->lfUnderline = mfp->underline;
\r
918 lf->lfStrikeOut = mfp->strikeout;
\r
919 lf->lfCharSet = mfp->charset;
\r
920 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
921 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
922 lf->lfQuality = DEFAULT_QUALITY;
\r
923 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
924 strcpy(lf->lfFaceName, mfp->faceName);
\r
928 CreateFontInMF(MyFont *mf)
\r
930 LFfromMFP(&mf->lf, &mf->mfp);
\r
931 if (mf->hf) DeleteObject(mf->hf);
\r
932 mf->hf = CreateFontIndirect(&mf->lf);
\r
935 // [HGM] This platform-dependent table provides the location for storing the color info
\r
937 colorVariable[] = {
\r
942 &highlightSquareColor,
\r
943 &premoveHighlightColor,
\r
945 &consoleBackgroundColor,
\r
946 &appData.fontForeColorWhite,
\r
947 &appData.fontBackColorWhite,
\r
948 &appData.fontForeColorBlack,
\r
949 &appData.fontBackColorBlack,
\r
950 &appData.evalHistColorWhite,
\r
951 &appData.evalHistColorBlack,
\r
952 &appData.highlightArrowColor,
\r
955 /* Command line font name parser. NULL name means do nothing.
\r
956 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
957 For backward compatibility, syntax without the colon is also
\r
958 accepted, but font names with digits in them won't work in that case.
\r
961 ParseFontName(char *name, MyFontParams *mfp)
\r
964 if (name == NULL) return;
\r
966 q = strchr(p, ':');
\r
968 if (q - p >= sizeof(mfp->faceName))
\r
969 ExitArgError("Font name too long:", name);
\r
970 memcpy(mfp->faceName, p, q - p);
\r
971 mfp->faceName[q - p] = NULLCHAR;
\r
975 while (*p && !isdigit(*p)) {
\r
977 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
978 ExitArgError("Font name too long:", name);
\r
980 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
983 if (!*p) ExitArgError("Font point size missing:", name);
\r
984 mfp->pointSize = (float) atof(p);
\r
985 mfp->bold = (strchr(p, 'b') != NULL);
\r
986 mfp->italic = (strchr(p, 'i') != NULL);
\r
987 mfp->underline = (strchr(p, 'u') != NULL);
\r
988 mfp->strikeout = (strchr(p, 's') != NULL);
\r
989 mfp->charset = DEFAULT_CHARSET;
\r
990 q = strchr(p, 'c');
\r
992 mfp->charset = (BYTE) atoi(q+1);
\r
996 ParseFont(char *name, int number)
\r
997 { // wrapper to shield back-end from 'font'
\r
998 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1003 { // in WB we have a 2D array of fonts; this initializes their description
\r
1005 /* Point font array elements to structures and
\r
1006 parse default font names */
\r
1007 for (i=0; i<NUM_FONTS; i++) {
\r
1008 for (j=0; j<NUM_SIZES; j++) {
\r
1009 font[j][i] = &fontRec[j][i];
\r
1010 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1017 { // here we create the actual fonts from the selected descriptions
\r
1019 for (i=0; i<NUM_FONTS; i++) {
\r
1020 for (j=0; j<NUM_SIZES; j++) {
\r
1021 CreateFontInMF(font[j][i]);
\r
1025 /* Color name parser.
\r
1026 X version accepts X color names, but this one
\r
1027 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1029 ParseColorName(char *name)
\r
1031 int red, green, blue, count;
\r
1032 char buf[MSG_SIZ];
\r
1034 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1036 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1037 &red, &green, &blue);
\r
1040 sprintf(buf, "Can't parse color name %s", name);
\r
1041 DisplayError(buf, 0);
\r
1042 return RGB(0, 0, 0);
\r
1044 return PALETTERGB(red, green, blue);
\r
1048 ParseColor(int n, char *name)
\r
1049 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1050 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1054 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1056 char *e = argValue;
\r
1060 if (*e == 'b') eff |= CFE_BOLD;
\r
1061 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1062 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1063 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1064 else if (*e == '#' || isdigit(*e)) break;
\r
1068 *color = ParseColorName(e);
\r
1072 ParseTextAttribs(ColorClass cc, char *s)
\r
1073 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1074 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1075 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1079 ParseBoardSize(void *addr, char *name)
\r
1080 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1081 BoardSize bs = SizeTiny;
\r
1082 while (sizeInfo[bs].name != NULL) {
\r
1083 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1084 *(BoardSize *)addr = bs;
\r
1089 ExitArgError("Unrecognized board size value", name);
\r
1094 { // [HGM] import name from appData first
\r
1097 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1098 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1099 textAttribs[cc].sound.data = NULL;
\r
1100 MyLoadSound(&textAttribs[cc].sound);
\r
1102 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1103 textAttribs[cc].sound.name = strdup("");
\r
1104 textAttribs[cc].sound.data = NULL;
\r
1106 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1107 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1108 sounds[sc].data = NULL;
\r
1109 MyLoadSound(&sounds[sc]);
\r
1114 SetCommPortDefaults()
\r
1116 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1117 dcb.DCBlength = sizeof(DCB);
\r
1118 dcb.BaudRate = 9600;
\r
1119 dcb.fBinary = TRUE;
\r
1120 dcb.fParity = FALSE;
\r
1121 dcb.fOutxCtsFlow = FALSE;
\r
1122 dcb.fOutxDsrFlow = FALSE;
\r
1123 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1124 dcb.fDsrSensitivity = FALSE;
\r
1125 dcb.fTXContinueOnXoff = TRUE;
\r
1126 dcb.fOutX = FALSE;
\r
1128 dcb.fNull = FALSE;
\r
1129 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1130 dcb.fAbortOnError = FALSE;
\r
1132 dcb.Parity = SPACEPARITY;
\r
1133 dcb.StopBits = ONESTOPBIT;
\r
1136 // [HGM] args: these three cases taken out to stay in front-end
\r
1138 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1139 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1140 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1141 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1143 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1144 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1145 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1146 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1147 ad->argName, mfp->faceName, mfp->pointSize,
\r
1148 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1149 mfp->bold ? "b" : "",
\r
1150 mfp->italic ? "i" : "",
\r
1151 mfp->underline ? "u" : "",
\r
1152 mfp->strikeout ? "s" : "",
\r
1153 (int)mfp->charset);
\r
1159 { // [HGM] copy the names from the internal WB variables to appData
\r
1162 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1163 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1164 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1165 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1169 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1170 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1171 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1172 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1173 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1174 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1175 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1176 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1177 (ta->effects) ? " " : "",
\r
1178 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1182 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1183 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1184 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1185 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1186 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1190 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1191 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1192 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1196 ParseCommPortSettings(char *s)
\r
1197 { // wrapper to keep dcb from back-end
\r
1198 ParseCommSettings(s, &dcb);
\r
1203 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1204 GetActualPlacement(hwndMain, &wpMain);
\r
1205 GetActualPlacement(hwndConsole, &wpConsole);
\r
1206 GetActualPlacement(commentDialog, &wpComment);
\r
1207 GetActualPlacement(editTagsDialog, &wpTags);
\r
1208 GetActualPlacement(gameListDialog, &wpGameList);
\r
1209 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1210 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1211 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1215 PrintCommPortSettings(FILE *f, char *name)
\r
1216 { // wrapper to shield back-end from DCB
\r
1217 PrintCommSettings(f, name, &dcb);
\r
1221 MySearchPath(char *installDir, char *name, char *fullname)
\r
1224 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1228 MyGetFullPathName(char *name, char *fullname)
\r
1231 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1236 { // [HGM] args: allows testing if main window is realized from back-end
\r
1237 return hwndMain != NULL;
\r
1241 PopUpStartupDialog()
\r
1245 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1246 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1247 FreeProcInstance(lpProc);
\r
1250 /*---------------------------------------------------------------------------*\
\r
1252 * GDI board drawing routines
\r
1254 \*---------------------------------------------------------------------------*/
\r
1256 /* [AS] Draw square using background texture */
\r
1257 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1262 return; /* Should never happen! */
\r
1265 SetGraphicsMode( dst, GM_ADVANCED );
\r
1272 /* X reflection */
\r
1277 x.eDx = (FLOAT) dw + dx - 1;
\r
1280 SetWorldTransform( dst, &x );
\r
1283 /* Y reflection */
\r
1289 x.eDy = (FLOAT) dh + dy - 1;
\r
1291 SetWorldTransform( dst, &x );
\r
1299 x.eDx = (FLOAT) dx;
\r
1300 x.eDy = (FLOAT) dy;
\r
1303 SetWorldTransform( dst, &x );
\r
1307 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1315 SetWorldTransform( dst, &x );
\r
1317 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1320 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1322 PM_WP = (int) WhitePawn,
\r
1323 PM_WN = (int) WhiteKnight,
\r
1324 PM_WB = (int) WhiteBishop,
\r
1325 PM_WR = (int) WhiteRook,
\r
1326 PM_WQ = (int) WhiteQueen,
\r
1327 PM_WF = (int) WhiteFerz,
\r
1328 PM_WW = (int) WhiteWazir,
\r
1329 PM_WE = (int) WhiteAlfil,
\r
1330 PM_WM = (int) WhiteMan,
\r
1331 PM_WO = (int) WhiteCannon,
\r
1332 PM_WU = (int) WhiteUnicorn,
\r
1333 PM_WH = (int) WhiteNightrider,
\r
1334 PM_WA = (int) WhiteAngel,
\r
1335 PM_WC = (int) WhiteMarshall,
\r
1336 PM_WAB = (int) WhiteCardinal,
\r
1337 PM_WD = (int) WhiteDragon,
\r
1338 PM_WL = (int) WhiteLance,
\r
1339 PM_WS = (int) WhiteCobra,
\r
1340 PM_WV = (int) WhiteFalcon,
\r
1341 PM_WSG = (int) WhiteSilver,
\r
1342 PM_WG = (int) WhiteGrasshopper,
\r
1343 PM_WK = (int) WhiteKing,
\r
1344 PM_BP = (int) BlackPawn,
\r
1345 PM_BN = (int) BlackKnight,
\r
1346 PM_BB = (int) BlackBishop,
\r
1347 PM_BR = (int) BlackRook,
\r
1348 PM_BQ = (int) BlackQueen,
\r
1349 PM_BF = (int) BlackFerz,
\r
1350 PM_BW = (int) BlackWazir,
\r
1351 PM_BE = (int) BlackAlfil,
\r
1352 PM_BM = (int) BlackMan,
\r
1353 PM_BO = (int) BlackCannon,
\r
1354 PM_BU = (int) BlackUnicorn,
\r
1355 PM_BH = (int) BlackNightrider,
\r
1356 PM_BA = (int) BlackAngel,
\r
1357 PM_BC = (int) BlackMarshall,
\r
1358 PM_BG = (int) BlackGrasshopper,
\r
1359 PM_BAB = (int) BlackCardinal,
\r
1360 PM_BD = (int) BlackDragon,
\r
1361 PM_BL = (int) BlackLance,
\r
1362 PM_BS = (int) BlackCobra,
\r
1363 PM_BV = (int) BlackFalcon,
\r
1364 PM_BSG = (int) BlackSilver,
\r
1365 PM_BK = (int) BlackKing
\r
1368 static HFONT hPieceFont = NULL;
\r
1369 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1370 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1371 static int fontBitmapSquareSize = 0;
\r
1372 static char pieceToFontChar[(int) EmptySquare] =
\r
1373 { 'p', 'n', 'b', 'r', 'q',
\r
1374 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1375 'k', 'o', 'm', 'v', 't', 'w',
\r
1376 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1379 extern BOOL SetCharTable( char *table, const char * map );
\r
1380 /* [HGM] moved to backend.c */
\r
1382 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1385 BYTE r1 = GetRValue( color );
\r
1386 BYTE g1 = GetGValue( color );
\r
1387 BYTE b1 = GetBValue( color );
\r
1393 /* Create a uniform background first */
\r
1394 hbrush = CreateSolidBrush( color );
\r
1395 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1396 FillRect( hdc, &rc, hbrush );
\r
1397 DeleteObject( hbrush );
\r
1400 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1401 int steps = squareSize / 2;
\r
1404 for( i=0; i<steps; i++ ) {
\r
1405 BYTE r = r1 - (r1-r2) * i / steps;
\r
1406 BYTE g = g1 - (g1-g2) * i / steps;
\r
1407 BYTE b = b1 - (b1-b2) * i / steps;
\r
1409 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1410 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1411 FillRect( hdc, &rc, hbrush );
\r
1412 DeleteObject(hbrush);
\r
1415 else if( mode == 2 ) {
\r
1416 /* Diagonal gradient, good more or less for every piece */
\r
1417 POINT triangle[3];
\r
1418 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1419 HBRUSH hbrush_old;
\r
1420 int steps = squareSize;
\r
1423 triangle[0].x = squareSize - steps;
\r
1424 triangle[0].y = squareSize;
\r
1425 triangle[1].x = squareSize;
\r
1426 triangle[1].y = squareSize;
\r
1427 triangle[2].x = squareSize;
\r
1428 triangle[2].y = squareSize - steps;
\r
1430 for( i=0; i<steps; i++ ) {
\r
1431 BYTE r = r1 - (r1-r2) * i / steps;
\r
1432 BYTE g = g1 - (g1-g2) * i / steps;
\r
1433 BYTE b = b1 - (b1-b2) * i / steps;
\r
1435 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1436 hbrush_old = SelectObject( hdc, hbrush );
\r
1437 Polygon( hdc, triangle, 3 );
\r
1438 SelectObject( hdc, hbrush_old );
\r
1439 DeleteObject(hbrush);
\r
1444 SelectObject( hdc, hpen );
\r
1449 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1450 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1451 piece: follow the steps as explained below.
\r
1453 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1457 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1461 int backColor = whitePieceColor;
\r
1462 int foreColor = blackPieceColor;
\r
1464 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1465 backColor = appData.fontBackColorWhite;
\r
1466 foreColor = appData.fontForeColorWhite;
\r
1468 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1469 backColor = appData.fontBackColorBlack;
\r
1470 foreColor = appData.fontForeColorBlack;
\r
1474 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1476 hbm_old = SelectObject( hdc, hbm );
\r
1480 rc.right = squareSize;
\r
1481 rc.bottom = squareSize;
\r
1483 /* Step 1: background is now black */
\r
1484 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1486 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1488 pt.x = (squareSize - sz.cx) / 2;
\r
1489 pt.y = (squareSize - sz.cy) / 2;
\r
1491 SetBkMode( hdc, TRANSPARENT );
\r
1492 SetTextColor( hdc, chroma );
\r
1493 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1494 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1496 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1497 /* Step 3: the area outside the piece is filled with white */
\r
1498 // FloodFill( hdc, 0, 0, chroma );
\r
1499 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1500 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1501 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1502 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1503 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1505 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1506 but if the start point is not inside the piece we're lost!
\r
1507 There should be a better way to do this... if we could create a region or path
\r
1508 from the fill operation we would be fine for example.
\r
1510 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1511 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1513 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1514 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1515 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1517 SelectObject( dc2, bm2 );
\r
1518 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1519 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1520 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1521 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1522 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1525 DeleteObject( bm2 );
\r
1528 SetTextColor( hdc, 0 );
\r
1530 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1531 draw the piece again in black for safety.
\r
1533 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1535 SelectObject( hdc, hbm_old );
\r
1537 if( hPieceMask[index] != NULL ) {
\r
1538 DeleteObject( hPieceMask[index] );
\r
1541 hPieceMask[index] = hbm;
\r
1544 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1546 SelectObject( hdc, hbm );
\r
1549 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1550 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1551 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1553 SelectObject( dc1, hPieceMask[index] );
\r
1554 SelectObject( dc2, bm2 );
\r
1555 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1556 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1559 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1560 the piece background and deletes (makes transparent) the rest.
\r
1561 Thanks to that mask, we are free to paint the background with the greates
\r
1562 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1563 We use this, to make gradients and give the pieces a "roundish" look.
\r
1565 SetPieceBackground( hdc, backColor, 2 );
\r
1566 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1570 DeleteObject( bm2 );
\r
1573 SetTextColor( hdc, foreColor );
\r
1574 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1576 SelectObject( hdc, hbm_old );
\r
1578 if( hPieceFace[index] != NULL ) {
\r
1579 DeleteObject( hPieceFace[index] );
\r
1582 hPieceFace[index] = hbm;
\r
1585 static int TranslatePieceToFontPiece( int piece )
\r
1615 case BlackMarshall:
\r
1619 case BlackNightrider:
\r
1625 case BlackUnicorn:
\r
1629 case BlackGrasshopper:
\r
1641 case BlackCardinal:
\r
1648 case WhiteMarshall:
\r
1652 case WhiteNightrider:
\r
1658 case WhiteUnicorn:
\r
1662 case WhiteGrasshopper:
\r
1674 case WhiteCardinal:
\r
1683 void CreatePiecesFromFont()
\r
1686 HDC hdc_window = NULL;
\r
1692 if( fontBitmapSquareSize < 0 ) {
\r
1693 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1697 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1698 fontBitmapSquareSize = -1;
\r
1702 if( fontBitmapSquareSize != squareSize ) {
\r
1703 hdc_window = GetDC( hwndMain );
\r
1704 hdc = CreateCompatibleDC( hdc_window );
\r
1706 if( hPieceFont != NULL ) {
\r
1707 DeleteObject( hPieceFont );
\r
1710 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1711 hPieceMask[i] = NULL;
\r
1712 hPieceFace[i] = NULL;
\r
1718 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1719 fontHeight = appData.fontPieceSize;
\r
1722 fontHeight = (fontHeight * squareSize) / 100;
\r
1724 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1726 lf.lfEscapement = 0;
\r
1727 lf.lfOrientation = 0;
\r
1728 lf.lfWeight = FW_NORMAL;
\r
1730 lf.lfUnderline = 0;
\r
1731 lf.lfStrikeOut = 0;
\r
1732 lf.lfCharSet = DEFAULT_CHARSET;
\r
1733 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1734 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1735 lf.lfQuality = PROOF_QUALITY;
\r
1736 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1737 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1738 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1740 hPieceFont = CreateFontIndirect( &lf );
\r
1742 if( hPieceFont == NULL ) {
\r
1743 fontBitmapSquareSize = -2;
\r
1746 /* Setup font-to-piece character table */
\r
1747 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1748 /* No (or wrong) global settings, try to detect the font */
\r
1749 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1751 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1753 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1754 /* DiagramTT* family */
\r
1755 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1757 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1758 /* Fairy symbols */
\r
1759 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1761 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1762 /* Good Companion (Some characters get warped as literal :-( */
\r
1763 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1764 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1765 SetCharTable(pieceToFontChar, s);
\r
1768 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1769 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1773 /* Create bitmaps */
\r
1774 hfont_old = SelectObject( hdc, hPieceFont );
\r
1775 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1776 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1777 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1779 SelectObject( hdc, hfont_old );
\r
1781 fontBitmapSquareSize = squareSize;
\r
1785 if( hdc != NULL ) {
\r
1789 if( hdc_window != NULL ) {
\r
1790 ReleaseDC( hwndMain, hdc_window );
\r
1795 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1799 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1800 if (gameInfo.event &&
\r
1801 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1802 strcmp(name, "k80s") == 0) {
\r
1803 strcpy(name, "tim");
\r
1805 return LoadBitmap(hinst, name);
\r
1809 /* Insert a color into the program's logical palette
\r
1810 structure. This code assumes the given color is
\r
1811 the result of the RGB or PALETTERGB macro, and it
\r
1812 knows how those macros work (which is documented).
\r
1815 InsertInPalette(COLORREF color)
\r
1817 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1819 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1820 DisplayFatalError("Too many colors", 0, 1);
\r
1821 pLogPal->palNumEntries--;
\r
1825 pe->peFlags = (char) 0;
\r
1826 pe->peRed = (char) (0xFF & color);
\r
1827 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1828 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1834 InitDrawingColors()
\r
1836 if (pLogPal == NULL) {
\r
1837 /* Allocate enough memory for a logical palette with
\r
1838 * PALETTESIZE entries and set the size and version fields
\r
1839 * of the logical palette structure.
\r
1841 pLogPal = (NPLOGPALETTE)
\r
1842 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1843 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1844 pLogPal->palVersion = 0x300;
\r
1846 pLogPal->palNumEntries = 0;
\r
1848 InsertInPalette(lightSquareColor);
\r
1849 InsertInPalette(darkSquareColor);
\r
1850 InsertInPalette(whitePieceColor);
\r
1851 InsertInPalette(blackPieceColor);
\r
1852 InsertInPalette(highlightSquareColor);
\r
1853 InsertInPalette(premoveHighlightColor);
\r
1855 /* create a logical color palette according the information
\r
1856 * in the LOGPALETTE structure.
\r
1858 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1860 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1861 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1862 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1863 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1864 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1865 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1866 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1867 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1868 /* [AS] Force rendering of the font-based pieces */
\r
1869 if( fontBitmapSquareSize > 0 ) {
\r
1870 fontBitmapSquareSize = 0;
\r
1876 BoardWidth(int boardSize, int n)
\r
1877 { /* [HGM] argument n added to allow different width and height */
\r
1878 int lineGap = sizeInfo[boardSize].lineGap;
\r
1880 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1881 lineGap = appData.overrideLineGap;
\r
1884 return (n + 1) * lineGap +
\r
1885 n * sizeInfo[boardSize].squareSize;
\r
1888 /* Respond to board resize by dragging edge */
\r
1890 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1892 BoardSize newSize = NUM_SIZES - 1;
\r
1893 static int recurse = 0;
\r
1894 if (IsIconic(hwndMain)) return;
\r
1895 if (recurse > 0) return;
\r
1897 while (newSize > 0) {
\r
1898 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1899 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1900 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1903 boardSize = newSize;
\r
1904 InitDrawingSizes(boardSize, flags);
\r
1909 extern Boolean twoBoards, partnerUp; // [HGM] dual
\r
1912 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1914 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1915 ChessSquare piece;
\r
1916 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1918 SIZE clockSize, messageSize;
\r
1920 char buf[MSG_SIZ];
\r
1922 HMENU hmenu = GetMenu(hwndMain);
\r
1923 RECT crect, wrect, oldRect;
\r
1925 LOGBRUSH logbrush;
\r
1927 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1928 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1930 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1931 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1933 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1934 oldRect.top = wpMain.y;
\r
1935 oldRect.right = wpMain.x + wpMain.width;
\r
1936 oldRect.bottom = wpMain.y + wpMain.height;
\r
1938 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1939 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1940 squareSize = sizeInfo[boardSize].squareSize;
\r
1941 lineGap = sizeInfo[boardSize].lineGap;
\r
1942 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1944 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1945 lineGap = appData.overrideLineGap;
\r
1948 if (tinyLayout != oldTinyLayout) {
\r
1949 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1951 style &= ~WS_SYSMENU;
\r
1952 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1953 "&Minimize\tCtrl+F4");
\r
1955 style |= WS_SYSMENU;
\r
1956 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1958 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1960 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1961 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1962 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1964 DrawMenuBar(hwndMain);
\r
1967 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1968 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1970 /* Get text area sizes */
\r
1971 hdc = GetDC(hwndMain);
\r
1972 if (appData.clockMode) {
\r
1973 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1975 sprintf(buf, "White");
\r
1977 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1978 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1979 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1980 str = "We only care about the height here";
\r
1981 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1982 SelectObject(hdc, oldFont);
\r
1983 ReleaseDC(hwndMain, hdc);
\r
1985 /* Compute where everything goes */
\r
1986 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1987 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1988 logoHeight = 2*clockSize.cy;
\r
1989 leftLogoRect.left = OUTER_MARGIN;
\r
1990 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1991 leftLogoRect.top = OUTER_MARGIN;
\r
1992 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1994 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1995 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1996 rightLogoRect.top = OUTER_MARGIN;
\r
1997 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2000 whiteRect.left = leftLogoRect.right;
\r
2001 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2002 whiteRect.top = OUTER_MARGIN;
\r
2003 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2005 blackRect.right = rightLogoRect.left;
\r
2006 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2007 blackRect.top = whiteRect.top;
\r
2008 blackRect.bottom = whiteRect.bottom;
\r
2010 whiteRect.left = OUTER_MARGIN;
\r
2011 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2012 whiteRect.top = OUTER_MARGIN;
\r
2013 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2017 blackRect.top = whiteRect.top;
\r
2018 blackRect.bottom = whiteRect.bottom;
\r
2020 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2023 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2024 if (appData.showButtonBar) {
\r
2025 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2026 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2028 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2030 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2031 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2033 boardRect.left = OUTER_MARGIN;
\r
2034 boardRect.right = boardRect.left + boardWidth;
\r
2035 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2036 boardRect.bottom = boardRect.top + boardHeight;
\r
2038 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2039 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2040 oldBoardSize = boardSize;
\r
2041 oldTinyLayout = tinyLayout;
\r
2042 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2043 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2044 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2045 winW *= 1 + twoBoards;
\r
2046 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2047 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2048 wpMain.height = winH; // without disturbing window attachments
\r
2049 GetWindowRect(hwndMain, &wrect);
\r
2050 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2051 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2053 // [HGM] placement: let attached windows follow size change.
\r
2054 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2055 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2056 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2057 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2058 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2060 /* compensate if menu bar wrapped */
\r
2061 GetClientRect(hwndMain, &crect);
\r
2062 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2063 wpMain.height += offby;
\r
2065 case WMSZ_TOPLEFT:
\r
2066 SetWindowPos(hwndMain, NULL,
\r
2067 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2068 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2071 case WMSZ_TOPRIGHT:
\r
2073 SetWindowPos(hwndMain, NULL,
\r
2074 wrect.left, wrect.bottom - wpMain.height,
\r
2075 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2078 case WMSZ_BOTTOMLEFT:
\r
2080 SetWindowPos(hwndMain, NULL,
\r
2081 wrect.right - wpMain.width, wrect.top,
\r
2082 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2085 case WMSZ_BOTTOMRIGHT:
\r
2089 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2090 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2095 for (i = 0; i < N_BUTTONS; i++) {
\r
2096 if (buttonDesc[i].hwnd != NULL) {
\r
2097 DestroyWindow(buttonDesc[i].hwnd);
\r
2098 buttonDesc[i].hwnd = NULL;
\r
2100 if (appData.showButtonBar) {
\r
2101 buttonDesc[i].hwnd =
\r
2102 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2103 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2104 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2105 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2106 (HMENU) buttonDesc[i].id,
\r
2107 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2109 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2110 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2111 MAKELPARAM(FALSE, 0));
\r
2113 if (buttonDesc[i].id == IDM_Pause)
\r
2114 hwndPause = buttonDesc[i].hwnd;
\r
2115 buttonDesc[i].wndproc = (WNDPROC)
\r
2116 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2119 if (gridPen != NULL) DeleteObject(gridPen);
\r
2120 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2121 if (premovePen != NULL) DeleteObject(premovePen);
\r
2122 if (lineGap != 0) {
\r
2123 logbrush.lbStyle = BS_SOLID;
\r
2124 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2126 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2127 lineGap, &logbrush, 0, NULL);
\r
2128 logbrush.lbColor = highlightSquareColor;
\r
2130 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2131 lineGap, &logbrush, 0, NULL);
\r
2133 logbrush.lbColor = premoveHighlightColor;
\r
2135 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2136 lineGap, &logbrush, 0, NULL);
\r
2138 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2139 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2140 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2141 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2142 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2143 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2144 BOARD_WIDTH * (squareSize + lineGap);
\r
2145 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2147 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2148 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2149 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2150 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2151 lineGap / 2 + (i * (squareSize + lineGap));
\r
2152 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2153 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2154 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2158 /* [HGM] Licensing requirement */
\r
2160 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2163 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2165 GothicPopUp( "", VariantNormal);
\r
2168 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2170 /* Load piece bitmaps for this board size */
\r
2171 for (i=0; i<=2; i++) {
\r
2172 for (piece = WhitePawn;
\r
2173 (int) piece < (int) BlackPawn;
\r
2174 piece = (ChessSquare) ((int) piece + 1)) {
\r
2175 if (pieceBitmap[i][piece] != NULL)
\r
2176 DeleteObject(pieceBitmap[i][piece]);
\r
2180 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2181 // Orthodox Chess pieces
\r
2182 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2183 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2184 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2185 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2186 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2187 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2188 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2189 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2190 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2191 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2192 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2193 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2194 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2195 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2196 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2197 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2198 // in Shogi, Hijack the unused Queen for Lance
\r
2199 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2200 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2201 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2203 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2204 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2205 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2208 if(squareSize <= 72 && squareSize >= 33) {
\r
2209 /* A & C are available in most sizes now */
\r
2210 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2211 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2212 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2213 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2214 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2215 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2216 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2217 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2218 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2219 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2220 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2221 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2222 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2223 } else { // Smirf-like
\r
2224 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2225 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2226 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2228 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2229 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2230 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2231 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2232 } else { // WinBoard standard
\r
2233 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2234 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2235 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2240 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2241 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2242 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2243 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2244 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2245 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2246 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2247 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2248 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2249 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2250 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2251 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2252 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2253 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2254 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2255 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2256 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2257 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2258 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2259 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2260 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2261 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2262 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2263 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2264 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2265 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2266 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2267 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2268 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2269 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2270 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2272 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2273 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2274 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2275 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2276 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2277 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2278 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2279 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2280 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2281 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2282 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2283 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2284 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2286 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2287 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2288 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2289 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2290 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2291 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2292 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2293 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2294 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2295 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2296 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2297 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2300 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2301 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2302 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2303 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2304 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2305 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2306 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2307 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2308 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2309 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2310 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2311 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2312 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2313 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2314 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2318 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2319 /* special Shogi support in this size */
\r
2320 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2321 for (piece = WhitePawn;
\r
2322 (int) piece < (int) BlackPawn;
\r
2323 piece = (ChessSquare) ((int) piece + 1)) {
\r
2324 if (pieceBitmap[i][piece] != NULL)
\r
2325 DeleteObject(pieceBitmap[i][piece]);
\r
2328 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2330 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2331 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2332 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2333 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2334 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2335 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2336 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2337 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2341 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2342 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2344 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2345 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2346 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2347 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2348 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2349 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2350 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2351 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2355 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2356 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2358 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2359 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2360 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2361 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2362 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2363 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2364 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2365 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2369 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2375 PieceBitmap(ChessSquare p, int kind)
\r
2377 if ((int) p >= (int) BlackPawn)
\r
2378 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2380 return pieceBitmap[kind][(int) p];
\r
2383 /***************************************************************/
\r
2385 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2386 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2388 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2389 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2393 SquareToPos(int row, int column, int * x, int * y)
\r
2396 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2397 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2399 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2400 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2405 DrawCoordsOnDC(HDC hdc)
\r
2407 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
2408 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
2409 char str[2] = { NULLCHAR, NULLCHAR };
\r
2410 int oldMode, oldAlign, x, y, start, i;
\r
2414 if (!appData.showCoords)
\r
2417 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2419 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2420 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2421 oldAlign = GetTextAlign(hdc);
\r
2422 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2424 y = boardRect.top + lineGap;
\r
2425 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2427 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2428 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2429 str[0] = files[start + i];
\r
2430 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2431 y += squareSize + lineGap;
\r
2434 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2436 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2437 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2438 str[0] = ranks[start + i];
\r
2439 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2440 x += squareSize + lineGap;
\r
2443 SelectObject(hdc, oldBrush);
\r
2444 SetBkMode(hdc, oldMode);
\r
2445 SetTextAlign(hdc, oldAlign);
\r
2446 SelectObject(hdc, oldFont);
\r
2450 DrawGridOnDC(HDC hdc)
\r
2454 if (lineGap != 0) {
\r
2455 oldPen = SelectObject(hdc, gridPen);
\r
2456 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2457 SelectObject(hdc, oldPen);
\r
2461 #define HIGHLIGHT_PEN 0
\r
2462 #define PREMOVE_PEN 1
\r
2465 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2468 HPEN oldPen, hPen;
\r
2469 if (lineGap == 0) return;
\r
2471 x1 = boardRect.left +
\r
2472 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2473 y1 = boardRect.top +
\r
2474 lineGap/2 + y * (squareSize + lineGap);
\r
2476 x1 = boardRect.left +
\r
2477 lineGap/2 + x * (squareSize + lineGap);
\r
2478 y1 = boardRect.top +
\r
2479 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2481 hPen = pen ? premovePen : highlightPen;
\r
2482 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2483 MoveToEx(hdc, x1, y1, NULL);
\r
2484 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2485 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2486 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2487 LineTo(hdc, x1, y1);
\r
2488 SelectObject(hdc, oldPen);
\r
2492 DrawHighlightsOnDC(HDC hdc)
\r
2495 for (i=0; i<2; i++) {
\r
2496 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2497 DrawHighlightOnDC(hdc, TRUE,
\r
2498 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2501 for (i=0; i<2; i++) {
\r
2502 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2503 premoveHighlightInfo.sq[i].y >= 0) {
\r
2504 DrawHighlightOnDC(hdc, TRUE,
\r
2505 premoveHighlightInfo.sq[i].x,
\r
2506 premoveHighlightInfo.sq[i].y,
\r
2512 /* Note: sqcolor is used only in monoMode */
\r
2513 /* Note that this code is largely duplicated in woptions.c,
\r
2514 function DrawSampleSquare, so that needs to be updated too */
\r
2516 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2518 HBITMAP oldBitmap;
\r
2522 if (appData.blindfold) return;
\r
2524 /* [AS] Use font-based pieces if needed */
\r
2525 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2526 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2527 CreatePiecesFromFont();
\r
2529 if( fontBitmapSquareSize == squareSize ) {
\r
2530 int index = TranslatePieceToFontPiece(piece);
\r
2532 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2536 squareSize, squareSize,
\r
2541 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2545 squareSize, squareSize,
\r
2554 if (appData.monoMode) {
\r
2555 SelectObject(tmphdc, PieceBitmap(piece,
\r
2556 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2557 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2558 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2560 tmpSize = squareSize;
\r
2562 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2563 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2564 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2565 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2566 x += (squareSize - minorSize)>>1;
\r
2567 y += squareSize - minorSize - 2;
\r
2568 tmpSize = minorSize;
\r
2570 if (color || appData.allWhite ) {
\r
2571 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2573 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2574 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2575 if(appData.upsideDown && color==flipView)
\r
2576 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2578 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2579 /* Use black for outline of white pieces */
\r
2580 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2581 if(appData.upsideDown && color==flipView)
\r
2582 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2584 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2586 /* Use square color for details of black pieces */
\r
2587 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2588 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2589 if(appData.upsideDown && !flipView)
\r
2590 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2592 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2594 SelectObject(hdc, oldBrush);
\r
2595 SelectObject(tmphdc, oldBitmap);
\r
2599 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2600 int GetBackTextureMode( int algo )
\r
2602 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2606 case BACK_TEXTURE_MODE_PLAIN:
\r
2607 result = 1; /* Always use identity map */
\r
2609 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2610 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2618 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2619 to handle redraws cleanly (as random numbers would always be different).
\r
2621 VOID RebuildTextureSquareInfo()
\r
2631 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2633 if( liteBackTexture != NULL ) {
\r
2634 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2635 lite_w = bi.bmWidth;
\r
2636 lite_h = bi.bmHeight;
\r
2640 if( darkBackTexture != NULL ) {
\r
2641 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2642 dark_w = bi.bmWidth;
\r
2643 dark_h = bi.bmHeight;
\r
2647 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2648 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2649 if( (col + row) & 1 ) {
\r
2651 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2652 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2653 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2654 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2659 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2660 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2661 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2662 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2669 /* [AS] Arrow highlighting support */
\r
2671 static int A_WIDTH = 5; /* Width of arrow body */
\r
2673 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2674 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2676 static double Sqr( double x )
\r
2681 static int Round( double x )
\r
2683 return (int) (x + 0.5);
\r
2686 /* Draw an arrow between two points using current settings */
\r
2687 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2690 double dx, dy, j, k, x, y;
\r
2692 if( d_x == s_x ) {
\r
2693 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2695 arrow[0].x = s_x + A_WIDTH;
\r
2698 arrow[1].x = s_x + A_WIDTH;
\r
2699 arrow[1].y = d_y - h;
\r
2701 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2702 arrow[2].y = d_y - h;
\r
2707 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2708 arrow[4].y = d_y - h;
\r
2710 arrow[5].x = s_x - A_WIDTH;
\r
2711 arrow[5].y = d_y - h;
\r
2713 arrow[6].x = s_x - A_WIDTH;
\r
2716 else if( d_y == s_y ) {
\r
2717 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2720 arrow[0].y = s_y + A_WIDTH;
\r
2722 arrow[1].x = d_x - w;
\r
2723 arrow[1].y = s_y + A_WIDTH;
\r
2725 arrow[2].x = d_x - w;
\r
2726 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2731 arrow[4].x = d_x - w;
\r
2732 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2734 arrow[5].x = d_x - w;
\r
2735 arrow[5].y = s_y - A_WIDTH;
\r
2738 arrow[6].y = s_y - A_WIDTH;
\r
2741 /* [AS] Needed a lot of paper for this! :-) */
\r
2742 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2743 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2745 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2747 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2752 arrow[0].x = Round(x - j);
\r
2753 arrow[0].y = Round(y + j*dx);
\r
2755 arrow[1].x = Round(x + j);
\r
2756 arrow[1].y = Round(y - j*dx);
\r
2759 x = (double) d_x - k;
\r
2760 y = (double) d_y - k*dy;
\r
2763 x = (double) d_x + k;
\r
2764 y = (double) d_y + k*dy;
\r
2767 arrow[2].x = Round(x + j);
\r
2768 arrow[2].y = Round(y - j*dx);
\r
2770 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2771 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2776 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2777 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2779 arrow[6].x = Round(x - j);
\r
2780 arrow[6].y = Round(y + j*dx);
\r
2783 Polygon( hdc, arrow, 7 );
\r
2786 /* [AS] Draw an arrow between two squares */
\r
2787 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2789 int s_x, s_y, d_x, d_y;
\r
2796 if( s_col == d_col && s_row == d_row ) {
\r
2800 /* Get source and destination points */
\r
2801 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2802 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2805 d_y += squareSize / 4;
\r
2807 else if( d_y < s_y ) {
\r
2808 d_y += 3 * squareSize / 4;
\r
2811 d_y += squareSize / 2;
\r
2815 d_x += squareSize / 4;
\r
2817 else if( d_x < s_x ) {
\r
2818 d_x += 3 * squareSize / 4;
\r
2821 d_x += squareSize / 2;
\r
2824 s_x += squareSize / 2;
\r
2825 s_y += squareSize / 2;
\r
2827 /* Adjust width */
\r
2828 A_WIDTH = squareSize / 14;
\r
2831 stLB.lbStyle = BS_SOLID;
\r
2832 stLB.lbColor = appData.highlightArrowColor;
\r
2835 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2836 holdpen = SelectObject( hdc, hpen );
\r
2837 hbrush = CreateBrushIndirect( &stLB );
\r
2838 holdbrush = SelectObject( hdc, hbrush );
\r
2840 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2842 SelectObject( hdc, holdpen );
\r
2843 SelectObject( hdc, holdbrush );
\r
2844 DeleteObject( hpen );
\r
2845 DeleteObject( hbrush );
\r
2848 BOOL HasHighlightInfo()
\r
2850 BOOL result = FALSE;
\r
2852 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2853 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2861 BOOL IsDrawArrowEnabled()
\r
2863 BOOL result = FALSE;
\r
2865 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2872 VOID DrawArrowHighlight( HDC hdc )
\r
2874 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2875 DrawArrowBetweenSquares( hdc,
\r
2876 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2877 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2881 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2883 HRGN result = NULL;
\r
2885 if( HasHighlightInfo() ) {
\r
2886 int x1, y1, x2, y2;
\r
2887 int sx, sy, dx, dy;
\r
2889 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2890 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2892 sx = MIN( x1, x2 );
\r
2893 sy = MIN( y1, y2 );
\r
2894 dx = MAX( x1, x2 ) + squareSize;
\r
2895 dy = MAX( y1, y2 ) + squareSize;
\r
2897 result = CreateRectRgn( sx, sy, dx, dy );
\r
2904 Warning: this function modifies the behavior of several other functions.
\r
2906 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2907 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2908 repaint is scattered all over the place, which is not good for features such as
\r
2909 "arrow highlighting" that require a full repaint of the board.
\r
2911 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2912 user interaction, when speed is not so important) but especially to avoid errors
\r
2913 in the displayed graphics.
\r
2915 In such patched places, I always try refer to this function so there is a single
\r
2916 place to maintain knowledge.
\r
2918 To restore the original behavior, just return FALSE unconditionally.
\r
2920 BOOL IsFullRepaintPreferrable()
\r
2922 BOOL result = FALSE;
\r
2924 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2925 /* Arrow may appear on the board */
\r
2933 This function is called by DrawPosition to know whether a full repaint must
\r
2936 Only DrawPosition may directly call this function, which makes use of
\r
2937 some state information. Other function should call DrawPosition specifying
\r
2938 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2940 BOOL DrawPositionNeedsFullRepaint()
\r
2942 BOOL result = FALSE;
\r
2945 Probably a slightly better policy would be to trigger a full repaint
\r
2946 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2947 but animation is fast enough that it's difficult to notice.
\r
2949 if( animInfo.piece == EmptySquare ) {
\r
2950 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2959 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2961 int row, column, x, y, square_color, piece_color;
\r
2962 ChessSquare piece;
\r
2964 HDC texture_hdc = NULL;
\r
2966 /* [AS] Initialize background textures if needed */
\r
2967 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2968 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2969 if( backTextureSquareSize != squareSize
\r
2970 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2971 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2972 backTextureSquareSize = squareSize;
\r
2973 RebuildTextureSquareInfo();
\r
2976 texture_hdc = CreateCompatibleDC( hdc );
\r
2979 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2980 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2982 SquareToPos(row, column, &x, &y);
\r
2984 piece = board[row][column];
\r
2986 square_color = ((column + row) % 2) == 1;
\r
2987 if( gameInfo.variant == VariantXiangqi ) {
\r
2988 square_color = !InPalace(row, column);
\r
2989 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2990 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2992 piece_color = (int) piece < (int) BlackPawn;
\r
2995 /* [HGM] holdings file: light square or black */
\r
2996 if(column == BOARD_LEFT-2) {
\r
2997 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
3000 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3004 if(column == BOARD_RGHT + 1 ) {
\r
3005 if( row < gameInfo.holdingsSize )
\r
3008 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3012 if(column == BOARD_LEFT-1 ) /* left align */
\r
3013 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3014 else if( column == BOARD_RGHT) /* right align */
\r
3015 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3017 if (appData.monoMode) {
\r
3018 if (piece == EmptySquare) {
\r
3019 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3020 square_color ? WHITENESS : BLACKNESS);
\r
3022 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3025 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3026 /* [AS] Draw the square using a texture bitmap */
\r
3027 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3028 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3029 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3032 squareSize, squareSize,
\r
3035 backTextureSquareInfo[r][c].mode,
\r
3036 backTextureSquareInfo[r][c].x,
\r
3037 backTextureSquareInfo[r][c].y );
\r
3039 SelectObject( texture_hdc, hbm );
\r
3041 if (piece != EmptySquare) {
\r
3042 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3046 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3048 oldBrush = SelectObject(hdc, brush );
\r
3049 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3050 SelectObject(hdc, oldBrush);
\r
3051 if (piece != EmptySquare)
\r
3052 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3057 if( texture_hdc != NULL ) {
\r
3058 DeleteDC( texture_hdc );
\r
3062 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3063 void fputDW(FILE *f, int x)
\r
3065 fputc(x & 255, f);
\r
3066 fputc(x>>8 & 255, f);
\r
3067 fputc(x>>16 & 255, f);
\r
3068 fputc(x>>24 & 255, f);
\r
3071 #define MAX_CLIPS 200 /* more than enough */
\r
3074 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3076 // HBITMAP bufferBitmap;
\r
3081 int w = 100, h = 50;
\r
3083 if(logo == NULL) return;
\r
3084 // GetClientRect(hwndMain, &Rect);
\r
3085 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3086 // Rect.bottom-Rect.top+1);
\r
3087 tmphdc = CreateCompatibleDC(hdc);
\r
3088 hbm = SelectObject(tmphdc, logo);
\r
3089 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3093 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3094 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3095 SelectObject(tmphdc, hbm);
\r
3099 static HDC hdcSeek;
\r
3101 // [HGM] seekgraph
\r
3102 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3105 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3106 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3107 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3108 SelectObject( hdcSeek, hp );
\r
3111 // front-end wrapper for drawing functions to do rectangles
\r
3112 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3117 if (hdcSeek == NULL) {
\r
3118 hdcSeek = GetDC(hwndMain);
\r
3119 if (!appData.monoMode) {
\r
3120 SelectPalette(hdcSeek, hPal, FALSE);
\r
3121 RealizePalette(hdcSeek);
\r
3124 hp = SelectObject( hdcSeek, gridPen );
\r
3125 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3126 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3127 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3128 SelectObject( hdcSeek, hp );
\r
3131 // front-end wrapper for putting text in graph
\r
3132 void DrawSeekText(char *buf, int x, int y)
\r
3135 SetBkMode( hdcSeek, TRANSPARENT );
\r
3136 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3137 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3140 void DrawSeekDot(int x, int y, int color)
\r
3142 int square = color & 0x80;
\r
3144 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3145 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3147 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3148 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3150 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3151 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3152 SelectObject(hdcSeek, oldBrush);
\r
3156 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3158 static Board lastReq, lastDrawn;
\r
3159 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3160 static int lastDrawnFlipView = 0;
\r
3161 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3162 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3165 HBITMAP bufferBitmap;
\r
3166 HBITMAP oldBitmap;
\r
3168 HRGN clips[MAX_CLIPS];
\r
3169 ChessSquare dragged_piece = EmptySquare;
\r
3171 /* I'm undecided on this - this function figures out whether a full
\r
3172 * repaint is necessary on its own, so there's no real reason to have the
\r
3173 * caller tell it that. I think this can safely be set to FALSE - but
\r
3174 * if we trust the callers not to request full repaints unnessesarily, then
\r
3175 * we could skip some clipping work. In other words, only request a full
\r
3176 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3177 * gamestart and similar) --Hawk
\r
3179 Boolean fullrepaint = repaint;
\r
3181 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3183 if( DrawPositionNeedsFullRepaint() ) {
\r
3184 fullrepaint = TRUE;
\r
3186 if(twoBoards) fullrepaint = TRUE; // [HGM] dual: our memory of last-drawn will be all wrong
\r
3188 if (board == NULL) {
\r
3189 if (!lastReqValid) {
\r
3194 CopyBoard(lastReq, board);
\r
3198 if (doingSizing) {
\r
3202 if (IsIconic(hwndMain)) {
\r
3206 if (hdc == NULL) {
\r
3207 hdc = GetDC(hwndMain);
\r
3208 if (!appData.monoMode) {
\r
3209 SelectPalette(hdc, hPal, FALSE);
\r
3210 RealizePalette(hdc);
\r
3214 releaseDC = FALSE;
\r
3217 /* Create some work-DCs */
\r
3218 hdcmem = CreateCompatibleDC(hdc);
\r
3219 tmphdc = CreateCompatibleDC(hdc);
\r
3221 /* If dragging is in progress, we temporarely remove the piece */
\r
3222 /* [HGM] or temporarily decrease count if stacked */
\r
3223 /* !! Moved to before board compare !! */
\r
3224 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3225 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3226 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3227 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3228 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3230 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3231 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3232 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3234 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3237 /* Figure out which squares need updating by comparing the
\r
3238 * newest board with the last drawn board and checking if
\r
3239 * flipping has changed.
\r
3241 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3242 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3243 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3244 if (lastDrawn[row][column] != board[row][column]) {
\r
3245 SquareToPos(row, column, &x, &y);
\r
3246 clips[num_clips++] =
\r
3247 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3251 for (i=0; i<2; i++) {
\r
3252 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3253 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3254 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3255 lastDrawnHighlight.sq[i].y >= 0) {
\r
3256 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3257 lastDrawnHighlight.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 (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3263 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3264 clips[num_clips++] =
\r
3265 CreateRectRgn(x - lineGap, y - lineGap,
\r
3266 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3270 for (i=0; i<2; i++) {
\r
3271 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3272 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3273 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3274 lastDrawnPremove.sq[i].y >= 0) {
\r
3275 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3276 lastDrawnPremove.sq[i].x, &x, &y);
\r
3277 clips[num_clips++] =
\r
3278 CreateRectRgn(x - lineGap, y - lineGap,
\r
3279 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3281 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3282 premoveHighlightInfo.sq[i].y >= 0) {
\r
3283 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3284 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3285 clips[num_clips++] =
\r
3286 CreateRectRgn(x - lineGap, y - lineGap,
\r
3287 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3292 fullrepaint = TRUE;
\r
3295 /* Create a buffer bitmap - this is the actual bitmap
\r
3296 * being written to. When all the work is done, we can
\r
3297 * copy it to the real DC (the screen). This avoids
\r
3298 * the problems with flickering.
\r
3300 GetClientRect(hwndMain, &Rect);
\r
3301 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3302 Rect.bottom-Rect.top+1);
\r
3303 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3304 if (!appData.monoMode) {
\r
3305 SelectPalette(hdcmem, hPal, FALSE);
\r
3308 /* Create clips for dragging */
\r
3309 if (!fullrepaint) {
\r
3310 if (dragInfo.from.x >= 0) {
\r
3311 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3312 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3314 if (dragInfo.start.x >= 0) {
\r
3315 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3316 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3318 if (dragInfo.pos.x >= 0) {
\r
3319 x = dragInfo.pos.x - squareSize / 2;
\r
3320 y = dragInfo.pos.y - squareSize / 2;
\r
3321 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3323 if (dragInfo.lastpos.x >= 0) {
\r
3324 x = dragInfo.lastpos.x - squareSize / 2;
\r
3325 y = dragInfo.lastpos.y - squareSize / 2;
\r
3326 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3330 /* Are we animating a move?
\r
3332 * - remove the piece from the board (temporarely)
\r
3333 * - calculate the clipping region
\r
3335 if (!fullrepaint) {
\r
3336 if (animInfo.piece != EmptySquare) {
\r
3337 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3338 x = boardRect.left + animInfo.lastpos.x;
\r
3339 y = boardRect.top + animInfo.lastpos.y;
\r
3340 x2 = boardRect.left + animInfo.pos.x;
\r
3341 y2 = boardRect.top + animInfo.pos.y;
\r
3342 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3343 /* Slight kludge. The real problem is that after AnimateMove is
\r
3344 done, the position on the screen does not match lastDrawn.
\r
3345 This currently causes trouble only on e.p. captures in
\r
3346 atomic, where the piece moves to an empty square and then
\r
3347 explodes. The old and new positions both had an empty square
\r
3348 at the destination, but animation has drawn a piece there and
\r
3349 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3350 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3354 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3355 if (num_clips == 0)
\r
3356 fullrepaint = TRUE;
\r
3358 /* Set clipping on the memory DC */
\r
3359 if (!fullrepaint) {
\r
3360 SelectClipRgn(hdcmem, clips[0]);
\r
3361 for (x = 1; x < num_clips; x++) {
\r
3362 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3363 abort(); // this should never ever happen!
\r
3367 /* Do all the drawing to the memory DC */
\r
3368 if(explodeInfo.radius) { // [HGM] atomic
\r
3370 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3371 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3372 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3373 x += squareSize/2;
\r
3374 y += squareSize/2;
\r
3375 if(!fullrepaint) {
\r
3376 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3377 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3379 DrawGridOnDC(hdcmem);
\r
3380 DrawHighlightsOnDC(hdcmem);
\r
3381 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3382 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3383 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3384 SelectObject(hdcmem, oldBrush);
\r
3386 DrawGridOnDC(hdcmem);
\r
3387 DrawHighlightsOnDC(hdcmem);
\r
3388 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3390 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3391 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3392 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3393 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3394 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3395 SquareToPos(row, column, &x, &y);
\r
3396 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3397 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3398 SelectObject(hdcmem, oldBrush);
\r
3403 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3404 if(appData.autoLogo) {
\r
3406 switch(gameMode) { // pick logos based on game mode
\r
3407 case IcsObserving:
\r
3408 whiteLogo = second.programLogo; // ICS logo
\r
3409 blackLogo = second.programLogo;
\r
3412 case IcsPlayingWhite:
\r
3413 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3414 blackLogo = second.programLogo; // ICS logo
\r
3416 case IcsPlayingBlack:
\r
3417 whiteLogo = second.programLogo; // ICS logo
\r
3418 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3420 case TwoMachinesPlay:
\r
3421 if(first.twoMachinesColor[0] == 'b') {
\r
3422 whiteLogo = second.programLogo;
\r
3423 blackLogo = first.programLogo;
\r
3426 case MachinePlaysWhite:
\r
3427 blackLogo = userLogo;
\r
3429 case MachinePlaysBlack:
\r
3430 whiteLogo = userLogo;
\r
3431 blackLogo = first.programLogo;
\r
3434 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3435 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3438 if( appData.highlightMoveWithArrow ) {
\r
3439 DrawArrowHighlight(hdcmem);
\r
3442 DrawCoordsOnDC(hdcmem);
\r
3444 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3445 /* to make sure lastDrawn contains what is actually drawn */
\r
3447 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3448 if (dragged_piece != EmptySquare) {
\r
3449 /* [HGM] or restack */
\r
3450 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3451 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3453 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3454 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3455 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3456 x = dragInfo.pos.x - squareSize / 2;
\r
3457 y = dragInfo.pos.y - squareSize / 2;
\r
3458 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3459 ((int) dragged_piece < (int) BlackPawn),
\r
3460 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3463 /* Put the animated piece back into place and draw it */
\r
3464 if (animInfo.piece != EmptySquare) {
\r
3465 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3466 x = boardRect.left + animInfo.pos.x;
\r
3467 y = boardRect.top + animInfo.pos.y;
\r
3468 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3469 ((int) animInfo.piece < (int) BlackPawn),
\r
3470 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3473 /* Release the bufferBitmap by selecting in the old bitmap
\r
3474 * and delete the memory DC
\r
3476 SelectObject(hdcmem, oldBitmap);
\r
3479 /* Set clipping on the target DC */
\r
3480 if (!fullrepaint) {
\r
3481 SelectClipRgn(hdc, clips[0]);
\r
3482 for (x = 1; x < num_clips; x++) {
\r
3483 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3484 abort(); // this should never ever happen!
\r
3488 /* Copy the new bitmap onto the screen in one go.
\r
3489 * This way we avoid any flickering
\r
3491 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3492 BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual
\r
3493 boardRect.right - boardRect.left,
\r
3494 boardRect.bottom - boardRect.top,
\r
3495 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3496 if(saveDiagFlag) {
\r
3497 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3498 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3500 GetObject(bufferBitmap, sizeof(b), &b);
\r
3501 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3502 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3503 bih.biWidth = b.bmWidth;
\r
3504 bih.biHeight = b.bmHeight;
\r
3506 bih.biBitCount = b.bmBitsPixel;
\r
3507 bih.biCompression = 0;
\r
3508 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3509 bih.biXPelsPerMeter = 0;
\r
3510 bih.biYPelsPerMeter = 0;
\r
3511 bih.biClrUsed = 0;
\r
3512 bih.biClrImportant = 0;
\r
3513 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3514 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3515 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3516 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3518 wb = b.bmWidthBytes;
\r
3520 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3521 int k = ((int*) pData)[i];
\r
3522 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3523 if(j >= 16) break;
\r
3525 if(j >= nrColors) nrColors = j+1;
\r
3527 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3529 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3530 for(w=0; w<(wb>>2); w+=2) {
\r
3531 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3532 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3533 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3534 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3535 pData[p++] = m | j<<4;
\r
3537 while(p&3) pData[p++] = 0;
\r
3540 wb = ((wb+31)>>5)<<2;
\r
3542 // write BITMAPFILEHEADER
\r
3543 fprintf(diagFile, "BM");
\r
3544 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3545 fputDW(diagFile, 0);
\r
3546 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3547 // write BITMAPINFOHEADER
\r
3548 fputDW(diagFile, 40);
\r
3549 fputDW(diagFile, b.bmWidth);
\r
3550 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3551 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3552 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3553 fputDW(diagFile, 0);
\r
3554 fputDW(diagFile, 0);
\r
3555 fputDW(diagFile, 0);
\r
3556 fputDW(diagFile, 0);
\r
3557 fputDW(diagFile, 0);
\r
3558 fputDW(diagFile, 0);
\r
3559 // write color table
\r
3561 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3562 // write bitmap data
\r
3563 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3564 fputc(pData[i], diagFile);
\r
3568 SelectObject(tmphdc, oldBitmap);
\r
3570 /* Massive cleanup */
\r
3571 for (x = 0; x < num_clips; x++)
\r
3572 DeleteObject(clips[x]);
\r
3575 DeleteObject(bufferBitmap);
\r
3578 ReleaseDC(hwndMain, hdc);
\r
3580 if (lastDrawnFlipView != flipView) {
\r
3582 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3584 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3587 /* CopyBoard(lastDrawn, board);*/
\r
3588 lastDrawnHighlight = highlightInfo;
\r
3589 lastDrawnPremove = premoveHighlightInfo;
\r
3590 lastDrawnFlipView = flipView;
\r
3591 lastDrawnValid = 1;
\r
3594 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3599 saveDiagFlag = 1; diagFile = f;
\r
3600 HDCDrawPosition(NULL, TRUE, NULL);
\r
3604 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3611 /*---------------------------------------------------------------------------*\
\r
3612 | CLIENT PAINT PROCEDURE
\r
3613 | This is the main event-handler for the WM_PAINT message.
\r
3615 \*---------------------------------------------------------------------------*/
\r
3617 PaintProc(HWND hwnd)
\r
3623 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3624 if (IsIconic(hwnd)) {
\r
3625 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3627 if (!appData.monoMode) {
\r
3628 SelectPalette(hdc, hPal, FALSE);
\r
3629 RealizePalette(hdc);
\r
3631 HDCDrawPosition(hdc, 1, NULL);
\r
3633 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3634 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3635 ETO_CLIPPED|ETO_OPAQUE,
\r
3636 &messageRect, messageText, strlen(messageText), NULL);
\r
3637 SelectObject(hdc, oldFont);
\r
3638 DisplayBothClocks();
\r
3640 EndPaint(hwnd,&ps);
\r
3648 * If the user selects on a border boundary, return -1; if off the board,
\r
3649 * return -2. Otherwise map the event coordinate to the square.
\r
3650 * The offset boardRect.left or boardRect.top must already have been
\r
3651 * subtracted from x.
\r
3653 int EventToSquare(x, limit)
\r
3661 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3663 x /= (squareSize + lineGap);
\r
3675 DropEnable dropEnables[] = {
\r
3676 { 'P', DP_Pawn, "Pawn" },
\r
3677 { 'N', DP_Knight, "Knight" },
\r
3678 { 'B', DP_Bishop, "Bishop" },
\r
3679 { 'R', DP_Rook, "Rook" },
\r
3680 { 'Q', DP_Queen, "Queen" },
\r
3684 SetupDropMenu(HMENU hmenu)
\r
3686 int i, count, enable;
\r
3688 extern char white_holding[], black_holding[];
\r
3689 char item[MSG_SIZ];
\r
3691 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3692 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3693 dropEnables[i].piece);
\r
3695 while (p && *p++ == dropEnables[i].piece) count++;
\r
3696 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3697 enable = count > 0 || !appData.testLegality
\r
3698 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3699 && !appData.icsActive);
\r
3700 ModifyMenu(hmenu, dropEnables[i].command,
\r
3701 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3702 dropEnables[i].command, item);
\r
3706 void DragPieceBegin(int x, int y)
\r
3708 dragInfo.lastpos.x = boardRect.left + x;
\r
3709 dragInfo.lastpos.y = boardRect.top + y;
\r
3710 dragInfo.from.x = fromX;
\r
3711 dragInfo.from.y = fromY;
\r
3712 dragInfo.start = dragInfo.from;
\r
3713 SetCapture(hwndMain);
\r
3716 void DragPieceEnd(int x, int y)
\r
3719 dragInfo.start.x = dragInfo.start.y = -1;
\r
3720 dragInfo.from = dragInfo.start;
\r
3721 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3724 /* Event handler for mouse messages */
\r
3726 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3730 static int recursive = 0;
\r
3732 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3735 if (message == WM_MBUTTONUP) {
\r
3736 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3737 to the middle button: we simulate pressing the left button too!
\r
3739 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3740 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3746 pt.x = LOWORD(lParam);
\r
3747 pt.y = HIWORD(lParam);
\r
3748 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3749 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3750 if (!flipView && y >= 0) {
\r
3751 y = BOARD_HEIGHT - 1 - y;
\r
3753 if (flipView && x >= 0) {
\r
3754 x = BOARD_WIDTH - 1 - x;
\r
3757 switch (message) {
\r
3758 case WM_LBUTTONDOWN:
\r
3759 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3760 if (gameMode == EditPosition) {
\r
3761 SetWhiteToPlayEvent();
\r
3762 } else if (gameMode == IcsPlayingBlack ||
\r
3763 gameMode == MachinePlaysWhite) {
\r
3765 } else if (gameMode == EditGame) {
\r
3766 AdjustClock(flipClock, -1);
\r
3768 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3769 if (gameMode == EditPosition) {
\r
3770 SetBlackToPlayEvent();
\r
3771 } else if (gameMode == IcsPlayingWhite ||
\r
3772 gameMode == MachinePlaysBlack) {
\r
3774 } else if (gameMode == EditGame) {
\r
3775 AdjustClock(!flipClock, -1);
\r
3778 dragInfo.start.x = dragInfo.start.y = -1;
\r
3779 dragInfo.from = dragInfo.start;
\r
3780 if(fromX == -1 && frozen) { // not sure where this is for
\r
3781 fromX = fromY = -1;
\r
3782 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3785 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3786 DrawPosition(TRUE, NULL);
\r
3789 case WM_LBUTTONUP:
\r
3790 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3791 DrawPosition(TRUE, NULL);
\r
3794 case WM_MOUSEMOVE:
\r
3795 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3796 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3797 if ((appData.animateDragging || appData.highlightDragging)
\r
3798 && (wParam & MK_LBUTTON)
\r
3799 && dragInfo.from.x >= 0)
\r
3801 BOOL full_repaint = FALSE;
\r
3803 if (appData.animateDragging) {
\r
3804 dragInfo.pos = pt;
\r
3806 if (appData.highlightDragging) {
\r
3807 SetHighlights(fromX, fromY, x, y);
\r
3808 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3809 full_repaint = TRUE;
\r
3813 DrawPosition( full_repaint, NULL);
\r
3815 dragInfo.lastpos = dragInfo.pos;
\r
3819 case WM_MOUSEWHEEL: // [DM]
\r
3820 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3821 /* Mouse Wheel is being rolled forward
\r
3822 * Play moves forward
\r
3824 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3825 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3826 /* Mouse Wheel is being rolled backward
\r
3827 * Play moves backward
\r
3829 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3830 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3834 case WM_MBUTTONUP:
\r
3835 case WM_RBUTTONUP:
\r
3837 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3840 case WM_MBUTTONDOWN:
\r
3841 case WM_RBUTTONDOWN:
\r
3844 fromX = fromY = -1;
\r
3845 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3846 dragInfo.start.x = dragInfo.start.y = -1;
\r
3847 dragInfo.from = dragInfo.start;
\r
3848 dragInfo.lastpos = dragInfo.pos;
\r
3849 if (appData.highlightDragging) {
\r
3850 ClearHighlights();
\r
3853 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3854 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3855 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3856 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3857 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3860 DrawPosition(TRUE, NULL);
\r
3862 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3865 if (message == WM_MBUTTONDOWN) {
\r
3866 buttonCount = 3; /* even if system didn't think so */
\r
3867 if (wParam & MK_SHIFT)
\r
3868 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3870 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3871 } else { /* message == WM_RBUTTONDOWN */
\r
3872 /* Just have one menu, on the right button. Windows users don't
\r
3873 think to try the middle one, and sometimes other software steals
\r
3874 it, or it doesn't really exist. */
\r
3875 if(gameInfo.variant != VariantShogi)
\r
3876 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3878 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3882 SetCapture(hwndMain);
3885 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3886 SetupDropMenu(hmenu);
\r
3887 MenuPopup(hwnd, pt, hmenu, -1);
\r
3897 /* Preprocess messages for buttons in main window */
\r
3899 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3901 int id = GetWindowLong(hwnd, GWL_ID);
\r
3904 for (i=0; i<N_BUTTONS; i++) {
\r
3905 if (buttonDesc[i].id == id) break;
\r
3907 if (i == N_BUTTONS) return 0;
\r
3908 switch (message) {
\r
3913 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3914 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3921 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3924 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3925 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3926 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3927 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3929 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3931 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3932 PopUpMoveDialog((char)wParam);
\r
3938 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3941 /* Process messages for Promotion dialog box */
\r
3943 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3947 switch (message) {
\r
3948 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3949 /* Center the dialog over the application window */
\r
3950 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3951 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3952 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3953 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3954 SW_SHOW : SW_HIDE);
\r
3955 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3956 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3957 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3958 PieceToChar(WhiteAngel) != '~') ||
\r
3959 (PieceToChar(BlackAngel) >= 'A' &&
\r
3960 PieceToChar(BlackAngel) != '~') ) ?
\r
3961 SW_SHOW : SW_HIDE);
\r
3962 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3963 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3964 PieceToChar(WhiteMarshall) != '~') ||
\r
3965 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3966 PieceToChar(BlackMarshall) != '~') ) ?
\r
3967 SW_SHOW : SW_HIDE);
\r
3968 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3969 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3970 gameInfo.variant != VariantShogi ?
\r
3971 SW_SHOW : SW_HIDE);
\r
3972 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3973 gameInfo.variant != VariantShogi ?
\r
3974 SW_SHOW : SW_HIDE);
\r
3975 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3976 gameInfo.variant == VariantShogi ?
\r
3977 SW_SHOW : SW_HIDE);
\r
3978 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3979 gameInfo.variant == VariantShogi ?
\r
3980 SW_SHOW : SW_HIDE);
\r
3981 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3982 gameInfo.variant == VariantSuper ?
\r
3983 SW_SHOW : SW_HIDE);
\r
3986 case WM_COMMAND: /* message: received a command */
\r
3987 switch (LOWORD(wParam)) {
\r
3989 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3990 ClearHighlights();
\r
3991 DrawPosition(FALSE, NULL);
\r
3994 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3997 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
4000 promoChar = PieceToChar(BlackRook);
\r
4003 promoChar = PieceToChar(BlackBishop);
\r
4005 case PB_Chancellor:
\r
4006 promoChar = PieceToChar(BlackMarshall);
\r
4008 case PB_Archbishop:
\r
4009 promoChar = PieceToChar(BlackAngel);
\r
4012 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4017 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4018 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4019 only show the popup when we are already sure the move is valid or
\r
4020 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4021 will figure out it is a promotion from the promoChar. */
\r
4022 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4023 fromX = fromY = -1;
\r
4024 if (!appData.highlightLastMove) {
\r
4025 ClearHighlights();
\r
4026 DrawPosition(FALSE, NULL);
\r
4033 /* Pop up promotion dialog */
\r
4035 PromotionPopup(HWND hwnd)
\r
4039 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4040 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4041 hwnd, (DLGPROC)lpProc);
\r
4042 FreeProcInstance(lpProc);
\r
4048 DrawPosition(TRUE, NULL);
\r
4049 PromotionPopup(hwndMain);
\r
4052 /* Toggle ShowThinking */
\r
4054 ToggleShowThinking()
\r
4056 appData.showThinking = !appData.showThinking;
\r
4057 ShowThinkingEvent();
\r
4061 LoadGameDialog(HWND hwnd, char* title)
\r
4065 char fileTitle[MSG_SIZ];
\r
4066 f = OpenFileDialog(hwnd, "rb", "",
\r
4067 appData.oldSaveStyle ? "gam" : "pgn",
\r
4069 title, &number, fileTitle, NULL);
\r
4071 cmailMsgLoaded = FALSE;
\r
4072 if (number == 0) {
\r
4073 int error = GameListBuild(f);
\r
4075 DisplayError("Cannot build game list", error);
\r
4076 } else if (!ListEmpty(&gameList) &&
\r
4077 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4078 GameListPopUp(f, fileTitle);
\r
4081 GameListDestroy();
\r
4084 LoadGame(f, number, fileTitle, FALSE);
\r
4088 int get_term_width()
\r
4093 HFONT hfont, hold_font;
\r
4098 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4102 // get the text metrics
\r
4103 hdc = GetDC(hText);
\r
4104 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4105 if (consoleCF.dwEffects & CFE_BOLD)
\r
4106 lf.lfWeight = FW_BOLD;
\r
4107 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4108 lf.lfItalic = TRUE;
\r
4109 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4110 lf.lfStrikeOut = TRUE;
\r
4111 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4112 lf.lfUnderline = TRUE;
\r
4113 hfont = CreateFontIndirect(&lf);
\r
4114 hold_font = SelectObject(hdc, hfont);
\r
4115 GetTextMetrics(hdc, &tm);
\r
4116 SelectObject(hdc, hold_font);
\r
4117 DeleteObject(hfont);
\r
4118 ReleaseDC(hText, hdc);
\r
4120 // get the rectangle
\r
4121 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4123 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4126 void UpdateICSWidth(HWND hText)
\r
4128 LONG old_width, new_width;
\r
4130 new_width = get_term_width(hText, FALSE);
\r
4131 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4132 if (new_width != old_width)
\r
4134 ics_update_width(new_width);
\r
4135 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4140 ChangedConsoleFont()
\r
4143 CHARRANGE tmpsel, sel;
\r
4144 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4145 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4146 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4149 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4150 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4151 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4152 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4153 * size. This was undocumented in the version of MSVC++ that I had
\r
4154 * when I wrote the code, but is apparently documented now.
\r
4156 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4157 cfmt.bCharSet = f->lf.lfCharSet;
\r
4158 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4159 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4160 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4161 /* Why are the following seemingly needed too? */
\r
4162 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4163 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4164 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4166 tmpsel.cpMax = -1; /*999999?*/
\r
4167 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4168 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4169 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4170 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4172 paraf.cbSize = sizeof(paraf);
\r
4173 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4174 paraf.dxStartIndent = 0;
\r
4175 paraf.dxOffset = WRAP_INDENT;
\r
4176 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4177 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4178 UpdateICSWidth(hText);
\r
4181 /*---------------------------------------------------------------------------*\
\r
4183 * Window Proc for main window
\r
4185 \*---------------------------------------------------------------------------*/
\r
4187 /* Process messages for main window, etc. */
\r
4189 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4192 int wmId, wmEvent;
\r
4196 char fileTitle[MSG_SIZ];
\r
4197 char buf[MSG_SIZ];
\r
4198 static SnapData sd;
\r
4200 switch (message) {
\r
4202 case WM_PAINT: /* message: repaint portion of window */
\r
4206 case WM_ERASEBKGND:
\r
4207 if (IsIconic(hwnd)) {
\r
4208 /* Cheat; change the message */
\r
4209 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4211 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4215 case WM_LBUTTONDOWN:
\r
4216 case WM_MBUTTONDOWN:
\r
4217 case WM_RBUTTONDOWN:
\r
4218 case WM_LBUTTONUP:
\r
4219 case WM_MBUTTONUP:
\r
4220 case WM_RBUTTONUP:
\r
4221 case WM_MOUSEMOVE:
\r
4222 case WM_MOUSEWHEEL:
\r
4223 MouseEvent(hwnd, message, wParam, lParam);
\r
4226 JAWS_KB_NAVIGATION
\r
4230 JAWS_ALT_INTERCEPT
\r
4232 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4233 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4234 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4235 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4237 SendMessage(h, message, wParam, lParam);
\r
4238 } else if(lParam != KF_REPEAT) {
\r
4239 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4240 PopUpMoveDialog((char)wParam);
\r
4241 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4242 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4247 case WM_PALETTECHANGED:
\r
4248 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4250 HDC hdc = GetDC(hwndMain);
\r
4251 SelectPalette(hdc, hPal, TRUE);
\r
4252 nnew = RealizePalette(hdc);
\r
4254 paletteChanged = TRUE;
\r
4255 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4257 ReleaseDC(hwnd, hdc);
\r
4261 case WM_QUERYNEWPALETTE:
\r
4262 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4264 HDC hdc = GetDC(hwndMain);
\r
4265 paletteChanged = FALSE;
\r
4266 SelectPalette(hdc, hPal, FALSE);
\r
4267 nnew = RealizePalette(hdc);
\r
4269 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4271 ReleaseDC(hwnd, hdc);
\r
4276 case WM_COMMAND: /* message: command from application menu */
\r
4277 wmId = LOWORD(wParam);
\r
4278 wmEvent = HIWORD(wParam);
\r
4283 SAY("new game enter a move to play against the computer with white");
\r
4286 case IDM_NewGameFRC:
\r
4287 if( NewGameFRC() == 0 ) {
\r
4292 case IDM_NewVariant:
\r
4293 NewVariantPopup(hwnd);
\r
4296 case IDM_LoadGame:
\r
4297 LoadGameDialog(hwnd, "Load Game from File");
\r
4300 case IDM_LoadNextGame:
\r
4304 case IDM_LoadPrevGame:
\r
4308 case IDM_ReloadGame:
\r
4312 case IDM_LoadPosition:
\r
4313 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4314 Reset(FALSE, TRUE);
\r
4317 f = OpenFileDialog(hwnd, "rb", "",
\r
4318 appData.oldSaveStyle ? "pos" : "fen",
\r
4320 "Load Position from File", &number, fileTitle, NULL);
\r
4322 LoadPosition(f, number, fileTitle);
\r
4326 case IDM_LoadNextPosition:
\r
4327 ReloadPosition(1);
\r
4330 case IDM_LoadPrevPosition:
\r
4331 ReloadPosition(-1);
\r
4334 case IDM_ReloadPosition:
\r
4335 ReloadPosition(0);
\r
4338 case IDM_SaveGame:
\r
4339 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4340 f = OpenFileDialog(hwnd, "a", defName,
\r
4341 appData.oldSaveStyle ? "gam" : "pgn",
\r
4343 "Save Game to File", NULL, fileTitle, NULL);
\r
4345 SaveGame(f, 0, "");
\r
4349 case IDM_SavePosition:
\r
4350 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4351 f = OpenFileDialog(hwnd, "a", defName,
\r
4352 appData.oldSaveStyle ? "pos" : "fen",
\r
4354 "Save Position to File", NULL, fileTitle, NULL);
\r
4356 SavePosition(f, 0, "");
\r
4360 case IDM_SaveDiagram:
\r
4361 defName = "diagram";
\r
4362 f = OpenFileDialog(hwnd, "wb", defName,
\r
4365 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4371 case IDM_CopyGame:
\r
4372 CopyGameToClipboard();
\r
4375 case IDM_PasteGame:
\r
4376 PasteGameFromClipboard();
\r
4379 case IDM_CopyGameListToClipboard:
\r
4380 CopyGameListToClipboard();
\r
4383 /* [AS] Autodetect FEN or PGN data */
\r
4384 case IDM_PasteAny:
\r
4385 PasteGameOrFENFromClipboard();
\r
4388 /* [AS] Move history */
\r
4389 case IDM_ShowMoveHistory:
\r
4390 if( MoveHistoryIsUp() ) {
\r
4391 MoveHistoryPopDown();
\r
4394 MoveHistoryPopUp();
\r
4398 /* [AS] Eval graph */
\r
4399 case IDM_ShowEvalGraph:
\r
4400 if( EvalGraphIsUp() ) {
\r
4401 EvalGraphPopDown();
\r
4405 SetFocus(hwndMain);
\r
4409 /* [AS] Engine output */
\r
4410 case IDM_ShowEngineOutput:
\r
4411 if( EngineOutputIsUp() ) {
\r
4412 EngineOutputPopDown();
\r
4415 EngineOutputPopUp();
\r
4419 /* [AS] User adjudication */
\r
4420 case IDM_UserAdjudication_White:
\r
4421 UserAdjudicationEvent( +1 );
\r
4424 case IDM_UserAdjudication_Black:
\r
4425 UserAdjudicationEvent( -1 );
\r
4428 case IDM_UserAdjudication_Draw:
\r
4429 UserAdjudicationEvent( 0 );
\r
4432 /* [AS] Game list options dialog */
\r
4433 case IDM_GameListOptions:
\r
4434 GameListOptions();
\r
4441 case IDM_CopyPosition:
\r
4442 CopyFENToClipboard();
\r
4445 case IDM_PastePosition:
\r
4446 PasteFENFromClipboard();
\r
4449 case IDM_MailMove:
\r
4453 case IDM_ReloadCMailMsg:
\r
4454 Reset(TRUE, TRUE);
\r
4455 ReloadCmailMsgEvent(FALSE);
\r
4458 case IDM_Minimize:
\r
4459 ShowWindow(hwnd, SW_MINIMIZE);
\r
4466 case IDM_MachineWhite:
\r
4467 MachineWhiteEvent();
\r
4469 * refresh the tags dialog only if it's visible
\r
4471 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4473 tags = PGNTags(&gameInfo);
\r
4474 TagsPopUp(tags, CmailMsg());
\r
4477 SAY("computer starts playing white");
\r
4480 case IDM_MachineBlack:
\r
4481 MachineBlackEvent();
\r
4483 * refresh the tags dialog only if it's visible
\r
4485 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4487 tags = PGNTags(&gameInfo);
\r
4488 TagsPopUp(tags, CmailMsg());
\r
4491 SAY("computer starts playing black");
\r
4494 case IDM_TwoMachines:
\r
4495 TwoMachinesEvent();
\r
4497 * refresh the tags dialog only if it's visible
\r
4499 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4501 tags = PGNTags(&gameInfo);
\r
4502 TagsPopUp(tags, CmailMsg());
\r
4505 SAY("programs start playing each other");
\r
4508 case IDM_AnalysisMode:
\r
4509 if (!first.analysisSupport) {
\r
4510 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4511 DisplayError(buf, 0);
\r
4513 SAY("analyzing current position");
\r
4514 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4515 if (appData.icsActive) {
\r
4516 if (gameMode != IcsObserving) {
\r
4517 sprintf(buf, "You are not observing a game");
\r
4518 DisplayError(buf, 0);
\r
4519 /* secure check */
\r
4520 if (appData.icsEngineAnalyze) {
\r
4521 if (appData.debugMode)
\r
4522 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4523 ExitAnalyzeMode();
\r
4529 /* if enable, user want disable icsEngineAnalyze */
\r
4530 if (appData.icsEngineAnalyze) {
\r
4531 ExitAnalyzeMode();
\r
4535 appData.icsEngineAnalyze = TRUE;
\r
4536 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4539 if (!appData.showThinking) ToggleShowThinking();
\r
4540 AnalyzeModeEvent();
\r
4544 case IDM_AnalyzeFile:
\r
4545 if (!first.analysisSupport) {
\r
4546 char buf[MSG_SIZ];
\r
4547 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4548 DisplayError(buf, 0);
\r
4550 if (!appData.showThinking) ToggleShowThinking();
\r
4551 AnalyzeFileEvent();
\r
4552 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4553 AnalysisPeriodicEvent(1);
\r
4557 case IDM_IcsClient:
\r
4561 case IDM_EditGame:
\r
4566 case IDM_EditPosition:
\r
4567 EditPositionEvent();
\r
4568 SAY("to set up a position type a FEN");
\r
4571 case IDM_Training:
\r
4575 case IDM_ShowGameList:
\r
4576 ShowGameListProc();
\r
4579 case IDM_EditTags:
\r
4583 case IDM_EditComment:
\r
4584 if (commentUp && editComment) {
\r
4587 EditCommentEvent();
\r
4607 case IDM_CallFlag:
\r
4627 case IDM_StopObserving:
\r
4628 StopObservingEvent();
\r
4631 case IDM_StopExamining:
\r
4632 StopExaminingEvent();
\r
4636 UploadGameEvent();
\r
4639 case IDM_TypeInMove:
\r
4640 PopUpMoveDialog('\000');
\r
4643 case IDM_TypeInName:
\r
4644 PopUpNameDialog('\000');
\r
4647 case IDM_Backward:
\r
4649 SetFocus(hwndMain);
\r
4656 SetFocus(hwndMain);
\r
4661 SetFocus(hwndMain);
\r
4666 SetFocus(hwndMain);
\r
4670 RevertEvent(FALSE);
\r
4673 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4674 RevertEvent(TRUE);
\r
4677 case IDM_TruncateGame:
\r
4678 TruncateGameEvent();
\r
4685 case IDM_RetractMove:
\r
4686 RetractMoveEvent();
\r
4689 case IDM_FlipView:
\r
4690 flipView = !flipView;
\r
4691 DrawPosition(FALSE, NULL);
\r
4694 case IDM_FlipClock:
\r
4695 flipClock = !flipClock;
\r
4696 DisplayBothClocks();
\r
4697 DrawPosition(FALSE, NULL);
\r
4700 case IDM_MuteSounds:
\r
4701 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4702 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4703 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4706 case IDM_GeneralOptions:
\r
4707 GeneralOptionsPopup(hwnd);
\r
4708 DrawPosition(TRUE, NULL);
\r
4711 case IDM_BoardOptions:
\r
4712 BoardOptionsPopup(hwnd);
\r
4715 case IDM_EnginePlayOptions:
\r
4716 EnginePlayOptionsPopup(hwnd);
\r
4719 case IDM_Engine1Options:
\r
4720 EngineOptionsPopup(hwnd, &first);
\r
4723 case IDM_Engine2Options:
\r
4724 EngineOptionsPopup(hwnd, &second);
\r
4727 case IDM_OptionsUCI:
\r
4728 UciOptionsPopup(hwnd);
\r
4731 case IDM_IcsOptions:
\r
4732 IcsOptionsPopup(hwnd);
\r
4736 FontsOptionsPopup(hwnd);
\r
4740 SoundOptionsPopup(hwnd);
\r
4743 case IDM_CommPort:
\r
4744 CommPortOptionsPopup(hwnd);
\r
4747 case IDM_LoadOptions:
\r
4748 LoadOptionsPopup(hwnd);
\r
4751 case IDM_SaveOptions:
\r
4752 SaveOptionsPopup(hwnd);
\r
4755 case IDM_TimeControl:
\r
4756 TimeControlOptionsPopup(hwnd);
\r
4759 case IDM_SaveSettings:
\r
4760 SaveSettings(settingsFileName);
\r
4763 case IDM_SaveSettingsOnExit:
\r
4764 saveSettingsOnExit = !saveSettingsOnExit;
\r
4765 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4766 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4767 MF_CHECKED : MF_UNCHECKED));
\r
4778 case IDM_AboutGame:
\r
4783 appData.debugMode = !appData.debugMode;
\r
4784 if (appData.debugMode) {
\r
4785 char dir[MSG_SIZ];
\r
4786 GetCurrentDirectory(MSG_SIZ, dir);
\r
4787 SetCurrentDirectory(installDir);
\r
4788 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4789 SetCurrentDirectory(dir);
\r
4790 setbuf(debugFP, NULL);
\r
4797 case IDM_HELPCONTENTS:
\r
4798 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4799 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4800 MessageBox (GetFocus(),
\r
4801 "Unable to activate help",
\r
4802 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4806 case IDM_HELPSEARCH:
\r
4807 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4808 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4809 MessageBox (GetFocus(),
\r
4810 "Unable to activate help",
\r
4811 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4815 case IDM_HELPHELP:
\r
4816 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4817 MessageBox (GetFocus(),
\r
4818 "Unable to activate help",
\r
4819 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4824 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4826 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4827 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4828 FreeProcInstance(lpProc);
\r
4831 case IDM_DirectCommand1:
\r
4832 AskQuestionEvent("Direct Command",
\r
4833 "Send to chess program:", "", "1");
\r
4835 case IDM_DirectCommand2:
\r
4836 AskQuestionEvent("Direct Command",
\r
4837 "Send to second chess program:", "", "2");
\r
4840 case EP_WhitePawn:
\r
4841 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4842 fromX = fromY = -1;
\r
4845 case EP_WhiteKnight:
\r
4846 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4847 fromX = fromY = -1;
\r
4850 case EP_WhiteBishop:
\r
4851 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4852 fromX = fromY = -1;
\r
4855 case EP_WhiteRook:
\r
4856 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4857 fromX = fromY = -1;
\r
4860 case EP_WhiteQueen:
\r
4861 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4862 fromX = fromY = -1;
\r
4865 case EP_WhiteFerz:
\r
4866 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4867 fromX = fromY = -1;
\r
4870 case EP_WhiteWazir:
\r
4871 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4872 fromX = fromY = -1;
\r
4875 case EP_WhiteAlfil:
\r
4876 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4877 fromX = fromY = -1;
\r
4880 case EP_WhiteCannon:
\r
4881 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4882 fromX = fromY = -1;
\r
4885 case EP_WhiteCardinal:
\r
4886 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4887 fromX = fromY = -1;
\r
4890 case EP_WhiteMarshall:
\r
4891 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4892 fromX = fromY = -1;
\r
4895 case EP_WhiteKing:
\r
4896 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4897 fromX = fromY = -1;
\r
4900 case EP_BlackPawn:
\r
4901 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4902 fromX = fromY = -1;
\r
4905 case EP_BlackKnight:
\r
4906 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4907 fromX = fromY = -1;
\r
4910 case EP_BlackBishop:
\r
4911 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4912 fromX = fromY = -1;
\r
4915 case EP_BlackRook:
\r
4916 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4917 fromX = fromY = -1;
\r
4920 case EP_BlackQueen:
\r
4921 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4922 fromX = fromY = -1;
\r
4925 case EP_BlackFerz:
\r
4926 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4927 fromX = fromY = -1;
\r
4930 case EP_BlackWazir:
\r
4931 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4932 fromX = fromY = -1;
\r
4935 case EP_BlackAlfil:
\r
4936 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4937 fromX = fromY = -1;
\r
4940 case EP_BlackCannon:
\r
4941 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4942 fromX = fromY = -1;
\r
4945 case EP_BlackCardinal:
\r
4946 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4947 fromX = fromY = -1;
\r
4950 case EP_BlackMarshall:
\r
4951 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4952 fromX = fromY = -1;
\r
4955 case EP_BlackKing:
\r
4956 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4957 fromX = fromY = -1;
\r
4960 case EP_EmptySquare:
\r
4961 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4962 fromX = fromY = -1;
\r
4965 case EP_ClearBoard:
\r
4966 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4967 fromX = fromY = -1;
\r
4971 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4972 fromX = fromY = -1;
\r
4976 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4977 fromX = fromY = -1;
\r
4981 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4982 fromX = fromY = -1;
\r
4986 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4987 fromX = fromY = -1;
\r
4991 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4992 fromX = fromY = -1;
\r
4996 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4997 fromX = fromY = -1;
\r
5001 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
5002 fromX = fromY = -1;
\r
5006 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5007 fromX = fromY = -1;
\r
5011 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5012 fromX = fromY = -1;
\r
5016 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5022 case CLOCK_TIMER_ID:
\r
5023 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5024 clockTimerEvent = 0;
\r
5025 DecrementClocks(); /* call into back end */
\r
5027 case LOAD_GAME_TIMER_ID:
\r
5028 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5029 loadGameTimerEvent = 0;
\r
5030 AutoPlayGameLoop(); /* call into back end */
\r
5032 case ANALYSIS_TIMER_ID:
\r
5033 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5034 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5035 AnalysisPeriodicEvent(0);
\r
5037 KillTimer(hwnd, analysisTimerEvent);
\r
5038 analysisTimerEvent = 0;
\r
5041 case DELAYED_TIMER_ID:
\r
5042 KillTimer(hwnd, delayedTimerEvent);
\r
5043 delayedTimerEvent = 0;
\r
5044 delayedTimerCallback();
\r
5049 case WM_USER_Input:
\r
5050 InputEvent(hwnd, message, wParam, lParam);
\r
5053 /* [AS] Also move "attached" child windows */
\r
5054 case WM_WINDOWPOSCHANGING:
\r
5056 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5057 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5059 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5060 /* Window is moving */
\r
5063 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5064 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5065 rcMain.right = wpMain.x + wpMain.width;
\r
5066 rcMain.top = wpMain.y;
\r
5067 rcMain.bottom = wpMain.y + wpMain.height;
\r
5069 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5070 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5071 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5072 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5073 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5074 wpMain.x = lpwp->x;
\r
5075 wpMain.y = lpwp->y;
\r
5080 /* [AS] Snapping */
\r
5081 case WM_ENTERSIZEMOVE:
\r
5082 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5083 if (hwnd == hwndMain) {
\r
5084 doingSizing = TRUE;
\r
5087 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5091 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5092 if (hwnd == hwndMain) {
\r
5093 lastSizing = wParam;
\r
5098 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5099 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5101 case WM_EXITSIZEMOVE:
\r
5102 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5103 if (hwnd == hwndMain) {
\r
5105 doingSizing = FALSE;
\r
5106 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5107 GetClientRect(hwnd, &client);
\r
5108 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5110 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5112 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5115 case WM_DESTROY: /* message: window being destroyed */
\r
5116 PostQuitMessage(0);
\r
5120 if (hwnd == hwndMain) {
\r
5125 default: /* Passes it on if unprocessed */
\r
5126 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5131 /*---------------------------------------------------------------------------*\
\r
5133 * Misc utility routines
\r
5135 \*---------------------------------------------------------------------------*/
\r
5138 * Decent random number generator, at least not as bad as Windows
\r
5139 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5141 unsigned int randstate;
\r
5146 randstate = randstate * 1664525 + 1013904223;
\r
5147 return (int) randstate & 0x7fffffff;
\r
5151 mysrandom(unsigned int seed)
\r
5158 * returns TRUE if user selects a different color, FALSE otherwise
\r
5162 ChangeColor(HWND hwnd, COLORREF *which)
\r
5164 static BOOL firstTime = TRUE;
\r
5165 static DWORD customColors[16];
\r
5167 COLORREF newcolor;
\r
5172 /* Make initial colors in use available as custom colors */
\r
5173 /* Should we put the compiled-in defaults here instead? */
\r
5175 customColors[i++] = lightSquareColor & 0xffffff;
\r
5176 customColors[i++] = darkSquareColor & 0xffffff;
\r
5177 customColors[i++] = whitePieceColor & 0xffffff;
\r
5178 customColors[i++] = blackPieceColor & 0xffffff;
\r
5179 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5180 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5182 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5183 customColors[i++] = textAttribs[ccl].color;
\r
5185 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5186 firstTime = FALSE;
\r
5189 cc.lStructSize = sizeof(cc);
\r
5190 cc.hwndOwner = hwnd;
\r
5191 cc.hInstance = NULL;
\r
5192 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5193 cc.lpCustColors = (LPDWORD) customColors;
\r
5194 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5196 if (!ChooseColor(&cc)) return FALSE;
\r
5198 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5199 if (newcolor == *which) return FALSE;
\r
5200 *which = newcolor;
\r
5204 InitDrawingColors();
\r
5205 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5210 MyLoadSound(MySound *ms)
\r
5216 if (ms->data) free(ms->data);
\r
5219 switch (ms->name[0]) {
\r
5225 /* System sound from Control Panel. Don't preload here. */
\r
5229 if (ms->name[1] == NULLCHAR) {
\r
5230 /* "!" alone = silence */
\r
5233 /* Builtin wave resource. Error if not found. */
\r
5234 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5235 if (h == NULL) break;
\r
5236 ms->data = (void *)LoadResource(hInst, h);
\r
5237 if (h == NULL) break;
\r
5242 /* .wav file. Error if not found. */
\r
5243 f = fopen(ms->name, "rb");
\r
5244 if (f == NULL) break;
\r
5245 if (fstat(fileno(f), &st) < 0) break;
\r
5246 ms->data = malloc(st.st_size);
\r
5247 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5253 char buf[MSG_SIZ];
\r
5254 sprintf(buf, "Error loading sound %s", ms->name);
\r
5255 DisplayError(buf, GetLastError());
\r
5261 MyPlaySound(MySound *ms)
\r
5263 BOOLEAN ok = FALSE;
\r
5265 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5266 switch (ms->name[0]) {
\r
5268 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5273 /* System sound from Control Panel (deprecated feature).
\r
5274 "$" alone or an unset sound name gets default beep (still in use). */
\r
5275 if (ms->name[1]) {
\r
5276 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5278 if (!ok) ok = MessageBeep(MB_OK);
\r
5281 /* Builtin wave resource, or "!" alone for silence */
\r
5282 if (ms->name[1]) {
\r
5283 if (ms->data == NULL) return FALSE;
\r
5284 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5290 /* .wav file. Error if not found. */
\r
5291 if (ms->data == NULL) return FALSE;
\r
5292 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5295 /* Don't print an error: this can happen innocently if the sound driver
\r
5296 is busy; for instance, if another instance of WinBoard is playing
\r
5297 a sound at about the same time. */
\r
5303 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5306 OPENFILENAME *ofn;
\r
5307 static UINT *number; /* gross that this is static */
\r
5309 switch (message) {
\r
5310 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5311 /* Center the dialog over the application window */
\r
5312 ofn = (OPENFILENAME *) lParam;
\r
5313 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5314 number = (UINT *) ofn->lCustData;
\r
5315 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5319 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5320 return FALSE; /* Allow for further processing */
\r
5323 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5324 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5326 return FALSE; /* Allow for further processing */
\r
5332 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5334 static UINT *number;
\r
5335 OPENFILENAME *ofname;
\r
5338 case WM_INITDIALOG:
\r
5339 ofname = (OPENFILENAME *)lParam;
\r
5340 number = (UINT *)(ofname->lCustData);
\r
5343 ofnot = (OFNOTIFY *)lParam;
\r
5344 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5345 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5354 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5355 char *nameFilt, char *dlgTitle, UINT *number,
\r
5356 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5358 OPENFILENAME openFileName;
\r
5359 char buf1[MSG_SIZ];
\r
5362 if (fileName == NULL) fileName = buf1;
\r
5363 if (defName == NULL) {
\r
5364 strcpy(fileName, "*.");
\r
5365 strcat(fileName, defExt);
\r
5367 strcpy(fileName, defName);
\r
5369 if (fileTitle) strcpy(fileTitle, "");
\r
5370 if (number) *number = 0;
\r
5372 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5373 openFileName.hwndOwner = hwnd;
\r
5374 openFileName.hInstance = (HANDLE) hInst;
\r
5375 openFileName.lpstrFilter = nameFilt;
\r
5376 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5377 openFileName.nMaxCustFilter = 0L;
\r
5378 openFileName.nFilterIndex = 1L;
\r
5379 openFileName.lpstrFile = fileName;
\r
5380 openFileName.nMaxFile = MSG_SIZ;
\r
5381 openFileName.lpstrFileTitle = fileTitle;
\r
5382 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5383 openFileName.lpstrInitialDir = NULL;
\r
5384 openFileName.lpstrTitle = dlgTitle;
\r
5385 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5386 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5387 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5388 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5389 openFileName.nFileOffset = 0;
\r
5390 openFileName.nFileExtension = 0;
\r
5391 openFileName.lpstrDefExt = defExt;
\r
5392 openFileName.lCustData = (LONG) number;
\r
5393 openFileName.lpfnHook = oldDialog ?
\r
5394 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5395 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5397 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5398 GetOpenFileName(&openFileName)) {
\r
5399 /* open the file */
\r
5400 f = fopen(openFileName.lpstrFile, write);
\r
5402 MessageBox(hwnd, "File open failed", NULL,
\r
5403 MB_OK|MB_ICONEXCLAMATION);
\r
5407 int err = CommDlgExtendedError();
\r
5408 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5417 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5419 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5422 * Get the first pop-up menu in the menu template. This is the
\r
5423 * menu that TrackPopupMenu displays.
\r
5425 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5427 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5430 * TrackPopup uses screen coordinates, so convert the
\r
5431 * coordinates of the mouse click to screen coordinates.
\r
5433 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5435 /* Draw and track the floating pop-up menu. */
\r
5436 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5437 pt.x, pt.y, 0, hwnd, NULL);
\r
5439 /* Destroy the menu.*/
\r
5440 DestroyMenu(hmenu);
\r
5445 int sizeX, sizeY, newSizeX, newSizeY;
\r
5447 } ResizeEditPlusButtonsClosure;
\r
5450 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5452 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5456 if (hChild == cl->hText) return TRUE;
\r
5457 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5458 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5459 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5460 ScreenToClient(cl->hDlg, &pt);
\r
5461 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5462 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5466 /* Resize a dialog that has a (rich) edit field filling most of
\r
5467 the top, with a row of buttons below */
\r
5469 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5472 int newTextHeight, newTextWidth;
\r
5473 ResizeEditPlusButtonsClosure cl;
\r
5475 /*if (IsIconic(hDlg)) return;*/
\r
5476 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5478 cl.hdwp = BeginDeferWindowPos(8);
\r
5480 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5481 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5482 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5483 if (newTextHeight < 0) {
\r
5484 newSizeY += -newTextHeight;
\r
5485 newTextHeight = 0;
\r
5487 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5488 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5494 cl.newSizeX = newSizeX;
\r
5495 cl.newSizeY = newSizeY;
\r
5496 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5498 EndDeferWindowPos(cl.hdwp);
\r
5501 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5503 RECT rChild, rParent;
\r
5504 int wChild, hChild, wParent, hParent;
\r
5505 int wScreen, hScreen, xNew, yNew;
\r
5508 /* Get the Height and Width of the child window */
\r
5509 GetWindowRect (hwndChild, &rChild);
\r
5510 wChild = rChild.right - rChild.left;
\r
5511 hChild = rChild.bottom - rChild.top;
\r
5513 /* Get the Height and Width of the parent window */
\r
5514 GetWindowRect (hwndParent, &rParent);
\r
5515 wParent = rParent.right - rParent.left;
\r
5516 hParent = rParent.bottom - rParent.top;
\r
5518 /* Get the display limits */
\r
5519 hdc = GetDC (hwndChild);
\r
5520 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5521 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5522 ReleaseDC(hwndChild, hdc);
\r
5524 /* Calculate new X position, then adjust for screen */
\r
5525 xNew = rParent.left + ((wParent - wChild) /2);
\r
5528 } else if ((xNew+wChild) > wScreen) {
\r
5529 xNew = wScreen - wChild;
\r
5532 /* Calculate new Y position, then adjust for screen */
\r
5534 yNew = rParent.top + ((hParent - hChild) /2);
\r
5537 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5542 } else if ((yNew+hChild) > hScreen) {
\r
5543 yNew = hScreen - hChild;
\r
5546 /* Set it, and return */
\r
5547 return SetWindowPos (hwndChild, NULL,
\r
5548 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5551 /* Center one window over another */
\r
5552 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5554 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5557 /*---------------------------------------------------------------------------*\
\r
5559 * Startup Dialog functions
\r
5561 \*---------------------------------------------------------------------------*/
\r
5563 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5565 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5567 while (*cd != NULL) {
\r
5568 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5574 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5576 char buf1[MAX_ARG_LEN];
\r
5579 if (str[0] == '@') {
\r
5580 FILE* f = fopen(str + 1, "r");
\r
5582 DisplayFatalError(str + 1, errno, 2);
\r
5585 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5587 buf1[len] = NULLCHAR;
\r
5591 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5594 char buf[MSG_SIZ];
\r
5595 char *end = strchr(str, '\n');
\r
5596 if (end == NULL) return;
\r
5597 memcpy(buf, str, end - str);
\r
5598 buf[end - str] = NULLCHAR;
\r
5599 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5605 SetStartupDialogEnables(HWND hDlg)
\r
5607 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5608 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5609 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5610 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5611 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5612 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5613 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5614 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5615 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5616 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5617 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5618 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5619 IsDlgButtonChecked(hDlg, OPT_View));
\r
5623 QuoteForFilename(char *filename)
\r
5625 int dquote, space;
\r
5626 dquote = strchr(filename, '"') != NULL;
\r
5627 space = strchr(filename, ' ') != NULL;
\r
5628 if (dquote || space) {
\r
5640 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5642 char buf[MSG_SIZ];
\r
5645 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5646 q = QuoteForFilename(nthcp);
\r
5647 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5648 if (*nthdir != NULLCHAR) {
\r
5649 q = QuoteForFilename(nthdir);
\r
5650 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5652 if (*nthcp == NULLCHAR) {
\r
5653 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5654 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5655 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5656 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5661 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5663 char buf[MSG_SIZ];
\r
5667 switch (message) {
\r
5668 case WM_INITDIALOG:
\r
5669 /* Center the dialog */
\r
5670 CenterWindow (hDlg, GetDesktopWindow());
\r
5671 /* Initialize the dialog items */
\r
5672 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5673 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5674 firstChessProgramNames);
\r
5675 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5676 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5677 secondChessProgramNames);
\r
5678 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5679 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5680 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5681 if (*appData.icsHelper != NULLCHAR) {
\r
5682 char *q = QuoteForFilename(appData.icsHelper);
\r
5683 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5685 if (*appData.icsHost == NULLCHAR) {
\r
5686 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5687 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5688 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5689 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5690 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5693 if (appData.icsActive) {
\r
5694 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5696 else if (appData.noChessProgram) {
\r
5697 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5700 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5703 SetStartupDialogEnables(hDlg);
\r
5707 switch (LOWORD(wParam)) {
\r
5709 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5710 strcpy(buf, "/fcp=");
\r
5711 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5713 ParseArgs(StringGet, &p);
\r
5714 strcpy(buf, "/scp=");
\r
5715 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5717 ParseArgs(StringGet, &p);
\r
5718 appData.noChessProgram = FALSE;
\r
5719 appData.icsActive = FALSE;
\r
5720 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5721 strcpy(buf, "/ics /icshost=");
\r
5722 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5724 ParseArgs(StringGet, &p);
\r
5725 if (appData.zippyPlay) {
\r
5726 strcpy(buf, "/fcp=");
\r
5727 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5729 ParseArgs(StringGet, &p);
\r
5731 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5732 appData.noChessProgram = TRUE;
\r
5733 appData.icsActive = FALSE;
\r
5735 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5736 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5739 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5740 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5742 ParseArgs(StringGet, &p);
\r
5744 EndDialog(hDlg, TRUE);
\r
5751 case IDM_HELPCONTENTS:
\r
5752 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5753 MessageBox (GetFocus(),
\r
5754 "Unable to activate help",
\r
5755 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5760 SetStartupDialogEnables(hDlg);
\r
5768 /*---------------------------------------------------------------------------*\
\r
5770 * About box dialog functions
\r
5772 \*---------------------------------------------------------------------------*/
\r
5774 /* Process messages for "About" dialog box */
\r
5776 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5778 switch (message) {
\r
5779 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5780 /* Center the dialog over the application window */
\r
5781 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5782 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5786 case WM_COMMAND: /* message: received a command */
\r
5787 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5788 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5789 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5797 /*---------------------------------------------------------------------------*\
\r
5799 * Comment Dialog functions
\r
5801 \*---------------------------------------------------------------------------*/
\r
5804 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5806 static HANDLE hwndText = NULL;
\r
5807 int len, newSizeX, newSizeY, flags;
\r
5808 static int sizeX, sizeY;
\r
5813 switch (message) {
\r
5814 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5815 /* Initialize the dialog items */
\r
5816 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5817 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5818 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5819 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5820 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5821 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5822 SetWindowText(hDlg, commentTitle);
\r
5823 if (editComment) {
\r
5824 SetFocus(hwndText);
\r
5826 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5828 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5829 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5830 MAKELPARAM(FALSE, 0));
\r
5831 /* Size and position the dialog */
\r
5832 if (!commentDialog) {
\r
5833 commentDialog = hDlg;
\r
5834 flags = SWP_NOZORDER;
\r
5835 GetClientRect(hDlg, &rect);
\r
5836 sizeX = rect.right;
\r
5837 sizeY = rect.bottom;
\r
5838 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5839 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5840 WINDOWPLACEMENT wp;
\r
5841 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5842 wp.length = sizeof(WINDOWPLACEMENT);
\r
5844 wp.showCmd = SW_SHOW;
\r
5845 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5846 wp.rcNormalPosition.left = wpComment.x;
\r
5847 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5848 wp.rcNormalPosition.top = wpComment.y;
\r
5849 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5850 SetWindowPlacement(hDlg, &wp);
\r
5852 GetClientRect(hDlg, &rect);
\r
5853 newSizeX = rect.right;
\r
5854 newSizeY = rect.bottom;
\r
5855 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5856 newSizeX, newSizeY);
\r
5861 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5864 case WM_COMMAND: /* message: received a command */
\r
5865 switch (LOWORD(wParam)) {
\r
5867 if (editComment) {
\r
5869 /* Read changed options from the dialog box */
\r
5870 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5871 len = GetWindowTextLength(hwndText);
\r
5872 str = (char *) malloc(len + 1);
\r
5873 GetWindowText(hwndText, str, len + 1);
\r
5882 ReplaceComment(commentIndex, str);
\r
5889 case OPT_CancelComment:
\r
5893 case OPT_ClearComment:
\r
5894 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5897 case OPT_EditComment:
\r
5898 EditCommentEvent();
\r
5906 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5907 if( wParam == OPT_CommentText ) {
\r
5908 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5910 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5914 pt.x = LOWORD( lpMF->lParam );
\r
5915 pt.y = HIWORD( lpMF->lParam );
\r
5917 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5919 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5920 len = GetWindowTextLength(hwndText);
\r
5921 str = (char *) malloc(len + 1);
\r
5922 GetWindowText(hwndText, str, len + 1);
\r
5923 ReplaceComment(commentIndex, str);
\r
5924 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5925 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5928 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5929 lpMF->msg = WM_USER;
\r
5937 newSizeX = LOWORD(lParam);
\r
5938 newSizeY = HIWORD(lParam);
\r
5939 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5944 case WM_GETMINMAXINFO:
\r
5945 /* Prevent resizing window too small */
\r
5946 mmi = (MINMAXINFO *) lParam;
\r
5947 mmi->ptMinTrackSize.x = 100;
\r
5948 mmi->ptMinTrackSize.y = 100;
\r
5955 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5960 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5962 if (str == NULL) str = "";
\r
5963 p = (char *) malloc(2 * strlen(str) + 2);
\r
5966 if (*str == '\n') *q++ = '\r';
\r
5970 if (commentText != NULL) free(commentText);
\r
5972 commentIndex = index;
\r
5973 commentTitle = title;
\r
5975 editComment = edit;
\r
5977 if (commentDialog) {
\r
5978 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5979 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5981 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5982 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5983 hwndMain, (DLGPROC)lpProc);
\r
5984 FreeProcInstance(lpProc);
\r
5990 /*---------------------------------------------------------------------------*\
\r
5992 * Type-in move dialog functions
\r
5994 \*---------------------------------------------------------------------------*/
\r
5997 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5999 char move[MSG_SIZ];
\r
6001 ChessMove moveType;
\r
6002 int fromX, fromY, toX, toY;
\r
6005 switch (message) {
\r
6006 case WM_INITDIALOG:
\r
6007 move[0] = (char) lParam;
\r
6008 move[1] = NULLCHAR;
\r
6009 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6010 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6011 SetWindowText(hInput, move);
\r
6013 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6017 switch (LOWORD(wParam)) {
\r
6019 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6020 { int n; Board board;
\r
6022 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6023 EditPositionPasteFEN(move);
\r
6024 EndDialog(hDlg, TRUE);
\r
6027 // [HGM] movenum: allow move number to be typed in any mode
\r
6028 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6030 EndDialog(hDlg, TRUE);
\r
6034 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6035 gameMode != Training) {
\r
6036 DisplayMoveError("Displayed move is not current");
\r
6038 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6039 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6040 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6041 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6042 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6043 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6044 if (gameMode != Training)
\r
6045 forwardMostMove = currentMove;
\r
6046 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6048 DisplayMoveError("Could not parse move");
\r
6051 EndDialog(hDlg, TRUE);
\r
6054 EndDialog(hDlg, FALSE);
\r
6065 PopUpMoveDialog(char firstchar)
\r
6069 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6070 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6071 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6072 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6073 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6074 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6075 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6076 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6077 gameMode == Training) {
\r
6078 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6079 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6080 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6081 FreeProcInstance(lpProc);
\r
6085 /*---------------------------------------------------------------------------*\
\r
6087 * Type-in name dialog functions
\r
6089 \*---------------------------------------------------------------------------*/
\r
6092 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6094 char move[MSG_SIZ];
\r
6097 switch (message) {
\r
6098 case WM_INITDIALOG:
\r
6099 move[0] = (char) lParam;
\r
6100 move[1] = NULLCHAR;
\r
6101 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6102 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6103 SetWindowText(hInput, move);
\r
6105 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6109 switch (LOWORD(wParam)) {
\r
6111 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6112 appData.userName = strdup(move);
\r
6115 EndDialog(hDlg, TRUE);
\r
6118 EndDialog(hDlg, FALSE);
\r
6129 PopUpNameDialog(char firstchar)
\r
6133 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6134 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6135 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6136 FreeProcInstance(lpProc);
\r
6139 /*---------------------------------------------------------------------------*\
\r
6143 \*---------------------------------------------------------------------------*/
\r
6145 /* Nonmodal error box */
\r
6146 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6147 WPARAM wParam, LPARAM lParam);
\r
6150 ErrorPopUp(char *title, char *content)
\r
6154 BOOLEAN modal = hwndMain == NULL;
\r
6172 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6173 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6176 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6178 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6179 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6180 hwndMain, (DLGPROC)lpProc);
\r
6181 FreeProcInstance(lpProc);
\r
6188 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6189 if (errorDialog == NULL) return;
\r
6190 DestroyWindow(errorDialog);
\r
6191 errorDialog = NULL;
\r
6192 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6196 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6201 switch (message) {
\r
6202 case WM_INITDIALOG:
\r
6203 GetWindowRect(hDlg, &rChild);
\r
6206 SetWindowPos(hDlg, NULL, rChild.left,
\r
6207 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6208 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6212 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6213 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6214 and it doesn't work when you resize the dialog.
\r
6215 For now, just give it a default position.
\r
6217 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6219 errorDialog = hDlg;
\r
6220 SetWindowText(hDlg, errorTitle);
\r
6221 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6222 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6226 switch (LOWORD(wParam)) {
\r
6229 if (errorDialog == hDlg) errorDialog = NULL;
\r
6230 DestroyWindow(hDlg);
\r
6242 HWND gothicDialog = NULL;
\r
6245 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6249 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6251 switch (message) {
\r
6252 case WM_INITDIALOG:
\r
6253 GetWindowRect(hDlg, &rChild);
\r
6255 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6259 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6260 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6261 and it doesn't work when you resize the dialog.
\r
6262 For now, just give it a default position.
\r
6264 gothicDialog = hDlg;
\r
6265 SetWindowText(hDlg, errorTitle);
\r
6266 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6267 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6271 switch (LOWORD(wParam)) {
\r
6274 if (errorDialog == hDlg) errorDialog = NULL;
\r
6275 DestroyWindow(hDlg);
\r
6287 GothicPopUp(char *title, VariantClass variant)
\r
6290 static char *lastTitle;
\r
6292 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6293 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6295 if(lastTitle != title && gothicDialog != NULL) {
\r
6296 DestroyWindow(gothicDialog);
\r
6297 gothicDialog = NULL;
\r
6299 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6300 title = lastTitle;
\r
6301 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6302 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6303 hwndMain, (DLGPROC)lpProc);
\r
6304 FreeProcInstance(lpProc);
\r
6309 /*---------------------------------------------------------------------------*\
\r
6311 * Ics Interaction console functions
\r
6313 \*---------------------------------------------------------------------------*/
\r
6315 #define HISTORY_SIZE 64
\r
6316 static char *history[HISTORY_SIZE];
\r
6317 int histIn = 0, histP = 0;
\r
6320 SaveInHistory(char *cmd)
\r
6322 if (history[histIn] != NULL) {
\r
6323 free(history[histIn]);
\r
6324 history[histIn] = NULL;
\r
6326 if (*cmd == NULLCHAR) return;
\r
6327 history[histIn] = StrSave(cmd);
\r
6328 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6329 if (history[histIn] != NULL) {
\r
6330 free(history[histIn]);
\r
6331 history[histIn] = NULL;
\r
6337 PrevInHistory(char *cmd)
\r
6340 if (histP == histIn) {
\r
6341 if (history[histIn] != NULL) free(history[histIn]);
\r
6342 history[histIn] = StrSave(cmd);
\r
6344 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6345 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6347 return history[histP];
\r
6353 if (histP == histIn) return NULL;
\r
6354 histP = (histP + 1) % HISTORY_SIZE;
\r
6355 return history[histP];
\r
6359 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6363 hmenu = LoadMenu(hInst, "TextMenu");
\r
6364 h = GetSubMenu(hmenu, 0);
\r
6366 if (strcmp(e->item, "-") == 0) {
\r
6367 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6368 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6369 int flags = MF_STRING, j = 0;
\r
6370 if (e->item[0] == '|') {
\r
6371 flags |= MF_MENUBARBREAK;
\r
6374 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6375 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6383 WNDPROC consoleTextWindowProc;
\r
6386 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6388 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6389 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6393 SetWindowText(hInput, command);
\r
6395 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6397 sel.cpMin = 999999;
\r
6398 sel.cpMax = 999999;
\r
6399 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6404 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6405 if (sel.cpMin == sel.cpMax) {
\r
6406 /* Expand to surrounding word */
\r
6409 tr.chrg.cpMax = sel.cpMin;
\r
6410 tr.chrg.cpMin = --sel.cpMin;
\r
6411 if (sel.cpMin < 0) break;
\r
6412 tr.lpstrText = name;
\r
6413 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6414 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6418 tr.chrg.cpMin = sel.cpMax;
\r
6419 tr.chrg.cpMax = ++sel.cpMax;
\r
6420 tr.lpstrText = name;
\r
6421 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6422 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6425 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6426 MessageBeep(MB_ICONEXCLAMATION);
\r
6430 tr.lpstrText = name;
\r
6431 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6433 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6434 MessageBeep(MB_ICONEXCLAMATION);
\r
6437 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6440 sprintf(buf, "%s %s", command, name);
\r
6441 SetWindowText(hInput, buf);
\r
6442 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6444 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6445 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6446 SetWindowText(hInput, buf);
\r
6447 sel.cpMin = 999999;
\r
6448 sel.cpMax = 999999;
\r
6449 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6455 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6460 switch (message) {
\r
6462 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6465 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6468 sel.cpMin = 999999;
\r
6469 sel.cpMax = 999999;
\r
6470 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6471 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6476 if(wParam != '\022') {
\r
6477 if (wParam == '\t') {
\r
6478 if (GetKeyState(VK_SHIFT) < 0) {
\r
6480 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6481 if (buttonDesc[0].hwnd) {
\r
6482 SetFocus(buttonDesc[0].hwnd);
\r
6484 SetFocus(hwndMain);
\r
6488 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6491 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6492 JAWS_DELETE( SetFocus(hInput); )
\r
6493 SendMessage(hInput, message, wParam, lParam);
\r
6496 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6497 case WM_RBUTTONDOWN:
\r
6498 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6499 /* Move selection here if it was empty */
\r
6501 pt.x = LOWORD(lParam);
\r
6502 pt.y = HIWORD(lParam);
\r
6503 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6504 if (sel.cpMin == sel.cpMax) {
\r
6505 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6506 sel.cpMax = sel.cpMin;
\r
6507 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6509 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6510 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6512 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6513 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6514 if (sel.cpMin == sel.cpMax) {
\r
6515 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6516 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6518 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6519 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6521 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6522 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6523 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6524 MenuPopup(hwnd, pt, hmenu, -1);
\r
6528 case WM_RBUTTONUP:
\r
6529 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6530 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6531 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6535 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6537 return SendMessage(hInput, message, wParam, lParam);
\r
6538 case WM_MBUTTONDOWN:
\r
6539 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6541 switch (LOWORD(wParam)) {
\r
6542 case IDM_QuickPaste:
\r
6544 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6545 if (sel.cpMin == sel.cpMax) {
\r
6546 MessageBeep(MB_ICONEXCLAMATION);
\r
6549 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6550 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6551 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6556 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6559 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6562 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6566 int i = LOWORD(wParam) - IDM_CommandX;
\r
6567 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6568 icsTextMenuEntry[i].command != NULL) {
\r
6569 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6570 icsTextMenuEntry[i].getname,
\r
6571 icsTextMenuEntry[i].immediate);
\r
6579 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6582 WNDPROC consoleInputWindowProc;
\r
6585 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6587 char buf[MSG_SIZ];
\r
6589 static BOOL sendNextChar = FALSE;
\r
6590 static BOOL quoteNextChar = FALSE;
\r
6591 InputSource *is = consoleInputSource;
\r
6595 switch (message) {
\r
6597 if (!appData.localLineEditing || sendNextChar) {
\r
6598 is->buf[0] = (CHAR) wParam;
\r
6600 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6601 sendNextChar = FALSE;
\r
6604 if (quoteNextChar) {
\r
6605 buf[0] = (char) wParam;
\r
6606 buf[1] = NULLCHAR;
\r
6607 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6608 quoteNextChar = FALSE;
\r
6612 case '\r': /* Enter key */
\r
6613 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6614 if (consoleEcho) SaveInHistory(is->buf);
\r
6615 is->buf[is->count++] = '\n';
\r
6616 is->buf[is->count] = NULLCHAR;
\r
6617 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6618 if (consoleEcho) {
\r
6619 ConsoleOutput(is->buf, is->count, TRUE);
\r
6620 } else if (appData.localLineEditing) {
\r
6621 ConsoleOutput("\n", 1, TRUE);
\r
6624 case '\033': /* Escape key */
\r
6625 SetWindowText(hwnd, "");
\r
6626 cf.cbSize = sizeof(CHARFORMAT);
\r
6627 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6628 if (consoleEcho) {
\r
6629 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6631 cf.crTextColor = COLOR_ECHOOFF;
\r
6633 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6634 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6636 case '\t': /* Tab key */
\r
6637 if (GetKeyState(VK_SHIFT) < 0) {
\r
6639 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6642 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6643 if (buttonDesc[0].hwnd) {
\r
6644 SetFocus(buttonDesc[0].hwnd);
\r
6646 SetFocus(hwndMain);
\r
6650 case '\023': /* Ctrl+S */
\r
6651 sendNextChar = TRUE;
\r
6653 case '\021': /* Ctrl+Q */
\r
6654 quoteNextChar = TRUE;
\r
6664 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6665 p = PrevInHistory(buf);
\r
6667 SetWindowText(hwnd, p);
\r
6668 sel.cpMin = 999999;
\r
6669 sel.cpMax = 999999;
\r
6670 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6675 p = NextInHistory();
\r
6677 SetWindowText(hwnd, p);
\r
6678 sel.cpMin = 999999;
\r
6679 sel.cpMax = 999999;
\r
6680 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6686 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6690 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6694 case WM_MBUTTONDOWN:
\r
6695 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6696 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6698 case WM_RBUTTONUP:
\r
6699 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6700 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6701 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6705 hmenu = LoadMenu(hInst, "InputMenu");
\r
6706 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6707 if (sel.cpMin == sel.cpMax) {
\r
6708 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6709 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6711 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6712 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6714 pt.x = LOWORD(lParam);
\r
6715 pt.y = HIWORD(lParam);
\r
6716 MenuPopup(hwnd, pt, hmenu, -1);
\r
6720 switch (LOWORD(wParam)) {
\r
6722 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6724 case IDM_SelectAll:
\r
6726 sel.cpMax = -1; /*999999?*/
\r
6727 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6730 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6733 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6736 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6741 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6744 #define CO_MAX 100000
\r
6745 #define CO_TRIM 1000
\r
6748 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6750 static SnapData sd;
\r
6751 HWND hText, hInput;
\r
6753 static int sizeX, sizeY;
\r
6754 int newSizeX, newSizeY;
\r
6758 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6759 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6761 switch (message) {
\r
6763 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6765 ENLINK *pLink = (ENLINK*)lParam;
\r
6766 if (pLink->msg == WM_LBUTTONUP)
\r
6770 tr.chrg = pLink->chrg;
\r
6771 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6772 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6773 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6774 free(tr.lpstrText);
\r
6778 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6779 hwndConsole = hDlg;
\r
6781 consoleTextWindowProc = (WNDPROC)
\r
6782 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6783 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6784 consoleInputWindowProc = (WNDPROC)
\r
6785 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6786 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6787 Colorize(ColorNormal, TRUE);
\r
6788 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6789 ChangedConsoleFont();
\r
6790 GetClientRect(hDlg, &rect);
\r
6791 sizeX = rect.right;
\r
6792 sizeY = rect.bottom;
\r
6793 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6794 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6795 WINDOWPLACEMENT wp;
\r
6796 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6797 wp.length = sizeof(WINDOWPLACEMENT);
\r
6799 wp.showCmd = SW_SHOW;
\r
6800 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6801 wp.rcNormalPosition.left = wpConsole.x;
\r
6802 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6803 wp.rcNormalPosition.top = wpConsole.y;
\r
6804 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6805 SetWindowPlacement(hDlg, &wp);
\r
6808 // [HGM] Chessknight's change 2004-07-13
\r
6809 else { /* Determine Defaults */
\r
6810 WINDOWPLACEMENT wp;
\r
6811 wpConsole.x = wpMain.width + 1;
\r
6812 wpConsole.y = wpMain.y;
\r
6813 wpConsole.width = screenWidth - wpMain.width;
\r
6814 wpConsole.height = wpMain.height;
\r
6815 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6816 wp.length = sizeof(WINDOWPLACEMENT);
\r
6818 wp.showCmd = SW_SHOW;
\r
6819 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6820 wp.rcNormalPosition.left = wpConsole.x;
\r
6821 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6822 wp.rcNormalPosition.top = wpConsole.y;
\r
6823 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6824 SetWindowPlacement(hDlg, &wp);
\r
6827 // Allow hText to highlight URLs and send notifications on them
\r
6828 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6829 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6830 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6831 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6845 if (IsIconic(hDlg)) break;
\r
6846 newSizeX = LOWORD(lParam);
\r
6847 newSizeY = HIWORD(lParam);
\r
6848 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6849 RECT rectText, rectInput;
\r
6851 int newTextHeight, newTextWidth;
\r
6852 GetWindowRect(hText, &rectText);
\r
6853 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6854 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6855 if (newTextHeight < 0) {
\r
6856 newSizeY += -newTextHeight;
\r
6857 newTextHeight = 0;
\r
6859 SetWindowPos(hText, NULL, 0, 0,
\r
6860 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6861 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6862 pt.x = rectInput.left;
\r
6863 pt.y = rectInput.top + newSizeY - sizeY;
\r
6864 ScreenToClient(hDlg, &pt);
\r
6865 SetWindowPos(hInput, NULL,
\r
6866 pt.x, pt.y, /* needs client coords */
\r
6867 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6868 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6874 case WM_GETMINMAXINFO:
\r
6875 /* Prevent resizing window too small */
\r
6876 mmi = (MINMAXINFO *) lParam;
\r
6877 mmi->ptMinTrackSize.x = 100;
\r
6878 mmi->ptMinTrackSize.y = 100;
\r
6881 /* [AS] Snapping */
\r
6882 case WM_ENTERSIZEMOVE:
\r
6883 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6886 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6889 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6891 case WM_EXITSIZEMOVE:
\r
6892 UpdateICSWidth(hText);
\r
6893 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6896 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6904 if (hwndConsole) return;
\r
6905 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6906 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6911 ConsoleOutput(char* data, int length, int forceVisible)
\r
6916 char buf[CO_MAX+1];
\r
6919 static int delayLF = 0;
\r
6920 CHARRANGE savesel, sel;
\r
6922 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6930 while (length--) {
\r
6938 } else if (*p == '\007') {
\r
6939 MyPlaySound(&sounds[(int)SoundBell]);
\r
6946 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6947 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6948 /* Save current selection */
\r
6949 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6950 exlen = GetWindowTextLength(hText);
\r
6951 /* Find out whether current end of text is visible */
\r
6952 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6953 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6954 /* Trim existing text if it's too long */
\r
6955 if (exlen + (q - buf) > CO_MAX) {
\r
6956 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6959 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6960 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6962 savesel.cpMin -= trim;
\r
6963 savesel.cpMax -= trim;
\r
6964 if (exlen < 0) exlen = 0;
\r
6965 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6966 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6968 /* Append the new text */
\r
6969 sel.cpMin = exlen;
\r
6970 sel.cpMax = exlen;
\r
6971 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6972 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6973 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6974 if (forceVisible || exlen == 0 ||
\r
6975 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6976 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6977 /* Scroll to make new end of text visible if old end of text
\r
6978 was visible or new text is an echo of user typein */
\r
6979 sel.cpMin = 9999999;
\r
6980 sel.cpMax = 9999999;
\r
6981 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6982 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6983 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6984 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6986 if (savesel.cpMax == exlen || forceVisible) {
\r
6987 /* Move insert point to new end of text if it was at the old
\r
6988 end of text or if the new text is an echo of user typein */
\r
6989 sel.cpMin = 9999999;
\r
6990 sel.cpMax = 9999999;
\r
6991 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6993 /* Restore previous selection */
\r
6994 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6996 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7003 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7007 COLORREF oldFg, oldBg;
\r
7011 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7013 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7014 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7015 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7018 rect.right = x + squareSize;
\r
7020 rect.bottom = y + squareSize;
\r
7023 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7024 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7025 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7026 &rect, str, strlen(str), NULL);
\r
7028 (void) SetTextColor(hdc, oldFg);
\r
7029 (void) SetBkColor(hdc, oldBg);
\r
7030 (void) SelectObject(hdc, oldFont);
\r
7034 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7035 RECT *rect, char *color, char *flagFell)
\r
7039 COLORREF oldFg, oldBg;
\r
7042 if (appData.clockMode) {
\r
7044 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7046 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7053 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7054 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7056 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7057 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7059 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7063 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7064 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7065 rect, str, strlen(str), NULL);
\r
7066 if(logoHeight > 0 && appData.clockMode) {
\r
7068 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7069 r.top = rect->top + logoHeight/2;
\r
7070 r.left = rect->left;
\r
7071 r.right = rect->right;
\r
7072 r.bottom = rect->bottom;
\r
7073 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7074 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7075 &r, str, strlen(str), NULL);
\r
7077 (void) SetTextColor(hdc, oldFg);
\r
7078 (void) SetBkColor(hdc, oldBg);
\r
7079 (void) SelectObject(hdc, oldFont);
\r
7084 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7090 if( count <= 0 ) {
\r
7091 if (appData.debugMode) {
\r
7092 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7095 return ERROR_INVALID_USER_BUFFER;
\r
7098 ResetEvent(ovl->hEvent);
\r
7099 ovl->Offset = ovl->OffsetHigh = 0;
\r
7100 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7104 err = GetLastError();
\r
7105 if (err == ERROR_IO_PENDING) {
\r
7106 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7110 err = GetLastError();
\r
7117 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7122 ResetEvent(ovl->hEvent);
\r
7123 ovl->Offset = ovl->OffsetHigh = 0;
\r
7124 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7128 err = GetLastError();
\r
7129 if (err == ERROR_IO_PENDING) {
\r
7130 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7134 err = GetLastError();
\r
7140 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7141 void CheckForInputBufferFull( InputSource * is )
\r
7143 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7144 /* Look for end of line */
\r
7145 char * p = is->buf;
\r
7147 while( p < is->next && *p != '\n' ) {
\r
7151 if( p >= is->next ) {
\r
7152 if (appData.debugMode) {
\r
7153 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7156 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7157 is->count = (DWORD) -1;
\r
7158 is->next = is->buf;
\r
7164 InputThread(LPVOID arg)
\r
7169 is = (InputSource *) arg;
\r
7170 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7171 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7172 while (is->hThread != NULL) {
\r
7173 is->error = DoReadFile(is->hFile, is->next,
\r
7174 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7175 &is->count, &ovl);
\r
7176 if (is->error == NO_ERROR) {
\r
7177 is->next += is->count;
\r
7179 if (is->error == ERROR_BROKEN_PIPE) {
\r
7180 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7183 is->count = (DWORD) -1;
\r
7184 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7189 CheckForInputBufferFull( is );
\r
7191 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7193 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7195 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7198 CloseHandle(ovl.hEvent);
\r
7199 CloseHandle(is->hFile);
\r
7201 if (appData.debugMode) {
\r
7202 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7209 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7211 NonOvlInputThread(LPVOID arg)
\r
7218 is = (InputSource *) arg;
\r
7219 while (is->hThread != NULL) {
\r
7220 is->error = ReadFile(is->hFile, is->next,
\r
7221 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7222 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7223 if (is->error == NO_ERROR) {
\r
7224 /* Change CRLF to LF */
\r
7225 if (is->next > is->buf) {
\r
7227 i = is->count + 1;
\r
7235 if (prev == '\r' && *p == '\n') {
\r
7247 if (is->error == ERROR_BROKEN_PIPE) {
\r
7248 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7251 is->count = (DWORD) -1;
\r
7255 CheckForInputBufferFull( is );
\r
7257 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7259 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7261 if (is->count < 0) break; /* Quit on error */
\r
7263 CloseHandle(is->hFile);
\r
7268 SocketInputThread(LPVOID arg)
\r
7272 is = (InputSource *) arg;
\r
7273 while (is->hThread != NULL) {
\r
7274 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7275 if ((int)is->count == SOCKET_ERROR) {
\r
7276 is->count = (DWORD) -1;
\r
7277 is->error = WSAGetLastError();
\r
7279 is->error = NO_ERROR;
\r
7280 is->next += is->count;
\r
7281 if (is->count == 0 && is->second == is) {
\r
7282 /* End of file on stderr; quit with no message */
\r
7286 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7288 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7290 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7296 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7300 is = (InputSource *) lParam;
\r
7301 if (is->lineByLine) {
\r
7302 /* Feed in lines one by one */
\r
7303 char *p = is->buf;
\r
7305 while (q < is->next) {
\r
7306 if (*q++ == '\n') {
\r
7307 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7312 /* Move any partial line to the start of the buffer */
\r
7314 while (p < is->next) {
\r
7319 if (is->error != NO_ERROR || is->count == 0) {
\r
7320 /* Notify backend of the error. Note: If there was a partial
\r
7321 line at the end, it is not flushed through. */
\r
7322 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7325 /* Feed in the whole chunk of input at once */
\r
7326 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7327 is->next = is->buf;
\r
7331 /*---------------------------------------------------------------------------*\
\r
7333 * Menu enables. Used when setting various modes.
\r
7335 \*---------------------------------------------------------------------------*/
\r
7343 GreyRevert(Boolean grey)
\r
7344 { // [HGM] vari: for retracting variations in local mode
\r
7345 HMENU hmenu = GetMenu(hwndMain);
\r
7346 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7347 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7351 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7353 while (enab->item > 0) {
\r
7354 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7359 Enables gnuEnables[] = {
\r
7360 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7361 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7362 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7363 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7364 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7365 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7366 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7367 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7368 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7369 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7370 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7371 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7372 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7376 Enables icsEnables[] = {
\r
7377 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7378 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7379 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7380 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7381 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7382 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7383 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7384 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7385 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7386 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7387 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7388 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7389 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7390 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7391 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7392 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7397 Enables zippyEnables[] = {
\r
7398 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7399 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7400 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7401 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7406 Enables ncpEnables[] = {
\r
7407 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7408 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7409 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7410 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7411 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7412 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7413 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7415 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7416 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7417 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7418 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7419 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7420 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7421 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7422 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7424 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7425 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7426 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7430 Enables trainingOnEnables[] = {
\r
7431 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7437 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7442 Enables trainingOffEnables[] = {
\r
7443 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7444 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7445 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7446 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7447 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7448 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7449 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7450 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7454 /* These modify either ncpEnables or gnuEnables */
\r
7455 Enables cmailEnables[] = {
\r
7456 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7457 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7458 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7459 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7460 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7461 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7462 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7466 Enables machineThinkingEnables[] = {
\r
7467 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7471 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7472 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7473 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7474 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7475 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7476 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7477 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7479 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7480 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7481 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7485 Enables userThinkingEnables[] = {
\r
7486 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7487 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7488 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7489 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7490 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7491 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7492 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7493 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7494 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7495 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7496 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7497 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7498 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7499 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7500 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7504 /*---------------------------------------------------------------------------*\
\r
7506 * Front-end interface functions exported by XBoard.
\r
7507 * Functions appear in same order as prototypes in frontend.h.
\r
7509 \*---------------------------------------------------------------------------*/
\r
7513 static UINT prevChecked = 0;
\r
7514 static int prevPausing = 0;
\r
7517 if (pausing != prevPausing) {
\r
7518 prevPausing = pausing;
\r
7519 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7520 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7521 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7524 switch (gameMode) {
\r
7525 case BeginningOfGame:
\r
7526 if (appData.icsActive)
\r
7527 nowChecked = IDM_IcsClient;
\r
7528 else if (appData.noChessProgram)
\r
7529 nowChecked = IDM_EditGame;
\r
7531 nowChecked = IDM_MachineBlack;
\r
7533 case MachinePlaysBlack:
\r
7534 nowChecked = IDM_MachineBlack;
\r
7536 case MachinePlaysWhite:
\r
7537 nowChecked = IDM_MachineWhite;
\r
7539 case TwoMachinesPlay:
\r
7540 nowChecked = IDM_TwoMachines;
\r
7543 nowChecked = IDM_AnalysisMode;
\r
7546 nowChecked = IDM_AnalyzeFile;
\r
7549 nowChecked = IDM_EditGame;
\r
7551 case PlayFromGameFile:
\r
7552 nowChecked = IDM_LoadGame;
\r
7554 case EditPosition:
\r
7555 nowChecked = IDM_EditPosition;
\r
7558 nowChecked = IDM_Training;
\r
7560 case IcsPlayingWhite:
\r
7561 case IcsPlayingBlack:
\r
7562 case IcsObserving:
\r
7564 nowChecked = IDM_IcsClient;
\r
7571 if (prevChecked != 0)
\r
7572 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7573 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7574 if (nowChecked != 0)
\r
7575 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7576 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7578 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7579 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7580 MF_BYCOMMAND|MF_ENABLED);
\r
7582 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7583 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7586 prevChecked = nowChecked;
\r
7588 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7589 if (appData.icsActive) {
\r
7590 if (appData.icsEngineAnalyze) {
\r
7591 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7592 MF_BYCOMMAND|MF_CHECKED);
\r
7594 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7595 MF_BYCOMMAND|MF_UNCHECKED);
\r
7603 HMENU hmenu = GetMenu(hwndMain);
\r
7604 SetMenuEnables(hmenu, icsEnables);
\r
7605 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7606 MF_BYPOSITION|MF_ENABLED);
\r
7608 if (appData.zippyPlay) {
\r
7609 SetMenuEnables(hmenu, zippyEnables);
\r
7610 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7611 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7612 MF_BYCOMMAND|MF_ENABLED);
\r
7620 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7626 HMENU hmenu = GetMenu(hwndMain);
\r
7627 SetMenuEnables(hmenu, ncpEnables);
\r
7628 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7629 MF_BYPOSITION|MF_GRAYED);
\r
7630 DrawMenuBar(hwndMain);
\r
7636 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7640 SetTrainingModeOn()
\r
7643 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7644 for (i = 0; i < N_BUTTONS; i++) {
\r
7645 if (buttonDesc[i].hwnd != NULL)
\r
7646 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7651 VOID SetTrainingModeOff()
\r
7654 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7655 for (i = 0; i < N_BUTTONS; i++) {
\r
7656 if (buttonDesc[i].hwnd != NULL)
\r
7657 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7663 SetUserThinkingEnables()
\r
7665 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7669 SetMachineThinkingEnables()
\r
7671 HMENU hMenu = GetMenu(hwndMain);
\r
7672 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7674 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7676 if (gameMode == MachinePlaysBlack) {
\r
7677 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7678 } else if (gameMode == MachinePlaysWhite) {
\r
7679 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7680 } else if (gameMode == TwoMachinesPlay) {
\r
7681 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7687 DisplayTitle(char *str)
\r
7689 char title[MSG_SIZ], *host;
\r
7690 if (str[0] != NULLCHAR) {
\r
7691 strcpy(title, str);
\r
7692 } else if (appData.icsActive) {
\r
7693 if (appData.icsCommPort[0] != NULLCHAR)
\r
7696 host = appData.icsHost;
\r
7697 sprintf(title, "%s: %s", szTitle, host);
\r
7698 } else if (appData.noChessProgram) {
\r
7699 strcpy(title, szTitle);
\r
7701 strcpy(title, szTitle);
\r
7702 strcat(title, ": ");
\r
7703 strcat(title, first.tidy);
\r
7705 SetWindowText(hwndMain, title);
\r
7710 DisplayMessage(char *str1, char *str2)
\r
7714 int remain = MESSAGE_TEXT_MAX - 1;
\r
7717 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7718 messageText[0] = NULLCHAR;
\r
7720 len = strlen(str1);
\r
7721 if (len > remain) len = remain;
\r
7722 strncpy(messageText, str1, len);
\r
7723 messageText[len] = NULLCHAR;
\r
7726 if (*str2 && remain >= 2) {
\r
7728 strcat(messageText, " ");
\r
7731 len = strlen(str2);
\r
7732 if (len > remain) len = remain;
\r
7733 strncat(messageText, str2, len);
\r
7735 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7737 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7741 hdc = GetDC(hwndMain);
\r
7742 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7743 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7744 &messageRect, messageText, strlen(messageText), NULL);
\r
7745 (void) SelectObject(hdc, oldFont);
\r
7746 (void) ReleaseDC(hwndMain, hdc);
\r
7750 DisplayError(char *str, int error)
\r
7752 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7758 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7759 NULL, error, LANG_NEUTRAL,
\r
7760 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7762 sprintf(buf, "%s:\n%s", str, buf2);
\r
7764 ErrorMap *em = errmap;
\r
7765 while (em->err != 0 && em->err != error) em++;
\r
7766 if (em->err != 0) {
\r
7767 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7769 sprintf(buf, "%s:\nError code %d", str, error);
\r
7774 ErrorPopUp("Error", buf);
\r
7779 DisplayMoveError(char *str)
\r
7781 fromX = fromY = -1;
\r
7782 ClearHighlights();
\r
7783 DrawPosition(FALSE, NULL);
\r
7784 if (appData.popupMoveErrors) {
\r
7785 ErrorPopUp("Error", str);
\r
7787 DisplayMessage(str, "");
\r
7788 moveErrorMessageUp = TRUE;
\r
7793 DisplayFatalError(char *str, int error, int exitStatus)
\r
7795 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7797 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7800 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7801 NULL, error, LANG_NEUTRAL,
\r
7802 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7804 sprintf(buf, "%s:\n%s", str, buf2);
\r
7806 ErrorMap *em = errmap;
\r
7807 while (em->err != 0 && em->err != error) em++;
\r
7808 if (em->err != 0) {
\r
7809 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7811 sprintf(buf, "%s:\nError code %d", str, error);
\r
7816 if (appData.debugMode) {
\r
7817 fprintf(debugFP, "%s: %s\n", label, str);
\r
7819 if (appData.popupExitMessage) {
\r
7820 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7821 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7823 ExitEvent(exitStatus);
\r
7828 DisplayInformation(char *str)
\r
7830 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7835 DisplayNote(char *str)
\r
7837 ErrorPopUp("Note", str);
\r
7842 char *title, *question, *replyPrefix;
\r
7847 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7849 static QuestionParams *qp;
\r
7850 char reply[MSG_SIZ];
\r
7853 switch (message) {
\r
7854 case WM_INITDIALOG:
\r
7855 qp = (QuestionParams *) lParam;
\r
7856 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7857 SetWindowText(hDlg, qp->title);
\r
7858 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7859 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7863 switch (LOWORD(wParam)) {
\r
7865 strcpy(reply, qp->replyPrefix);
\r
7866 if (*reply) strcat(reply, " ");
\r
7867 len = strlen(reply);
\r
7868 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7869 strcat(reply, "\n");
\r
7870 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7871 EndDialog(hDlg, TRUE);
\r
7872 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7875 EndDialog(hDlg, FALSE);
\r
7886 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7888 QuestionParams qp;
\r
7892 qp.question = question;
\r
7893 qp.replyPrefix = replyPrefix;
\r
7895 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7896 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7897 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7898 FreeProcInstance(lpProc);
\r
7901 /* [AS] Pick FRC position */
\r
7902 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7904 static int * lpIndexFRC;
\r
7910 case WM_INITDIALOG:
\r
7911 lpIndexFRC = (int *) lParam;
\r
7913 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7915 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7916 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7917 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7918 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7923 switch( LOWORD(wParam) ) {
\r
7925 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7926 EndDialog( hDlg, 0 );
\r
7927 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7930 EndDialog( hDlg, 1 );
\r
7932 case IDC_NFG_Edit:
\r
7933 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7934 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7936 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7939 case IDC_NFG_Random:
\r
7940 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7941 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7954 int index = appData.defaultFrcPosition;
\r
7955 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7957 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7959 if( result == 0 ) {
\r
7960 appData.defaultFrcPosition = index;
\r
7966 /* [AS] Game list options. Refactored by HGM */
\r
7968 HWND gameListOptionsDialog;
\r
7970 // low-level front-end: clear text edit / list widget
\r
7974 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7977 // low-level front-end: clear text edit / list widget
\r
7979 GLT_DeSelectList()
\r
7981 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7984 // low-level front-end: append line to text edit / list widget
\r
7986 GLT_AddToList( char *name )
\r
7989 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
7993 // low-level front-end: get line from text edit / list widget
\r
7995 GLT_GetFromList( int index, char *name )
\r
7998 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8004 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8006 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8007 int idx2 = idx1 + delta;
\r
8008 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8010 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8013 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8014 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8015 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8016 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8020 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8024 case WM_INITDIALOG:
\r
8025 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8027 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8029 /* Initialize list */
\r
8030 GLT_TagsToList( lpUserGLT );
\r
8032 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8037 switch( LOWORD(wParam) ) {
\r
8040 EndDialog( hDlg, 0 );
\r
8043 EndDialog( hDlg, 1 );
\r
8046 case IDC_GLT_Default:
\r
8047 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8050 case IDC_GLT_Restore:
\r
8051 GLT_TagsToList( appData.gameListTags );
\r
8055 GLT_MoveSelection( hDlg, -1 );
\r
8058 case IDC_GLT_Down:
\r
8059 GLT_MoveSelection( hDlg, +1 );
\r
8069 int GameListOptions()
\r
8072 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8074 strcpy( lpUserGLT, appData.gameListTags );
\r
8076 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8078 if( result == 0 ) {
\r
8079 /* [AS] Memory leak here! */
\r
8080 appData.gameListTags = strdup( lpUserGLT );
\r
8087 DisplayIcsInteractionTitle(char *str)
\r
8089 char consoleTitle[MSG_SIZ];
\r
8091 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8092 SetWindowText(hwndConsole, consoleTitle);
\r
8096 DrawPosition(int fullRedraw, Board board)
\r
8098 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8101 void NotifyFrontendLogin()
\r
8104 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8110 fromX = fromY = -1;
\r
8111 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8112 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8113 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8114 dragInfo.lastpos = dragInfo.pos;
\r
8115 dragInfo.start.x = dragInfo.start.y = -1;
\r
8116 dragInfo.from = dragInfo.start;
\r
8118 DrawPosition(TRUE, NULL);
\r
8124 CommentPopUp(char *title, char *str)
\r
8126 HWND hwnd = GetActiveWindow();
\r
8127 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8129 SetActiveWindow(hwnd);
\r
8133 CommentPopDown(void)
\r
8135 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8136 if (commentDialog) {
\r
8137 ShowWindow(commentDialog, SW_HIDE);
\r
8139 commentUp = FALSE;
\r
8143 EditCommentPopUp(int index, char *title, char *str)
\r
8145 EitherCommentPopUp(index, title, str, TRUE);
\r
8152 MyPlaySound(&sounds[(int)SoundMove]);
\r
8155 VOID PlayIcsWinSound()
\r
8157 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8160 VOID PlayIcsLossSound()
\r
8162 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8165 VOID PlayIcsDrawSound()
\r
8167 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8170 VOID PlayIcsUnfinishedSound()
\r
8172 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8178 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8186 consoleEcho = TRUE;
\r
8187 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8188 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8189 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8198 consoleEcho = FALSE;
\r
8199 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8200 /* This works OK: set text and background both to the same color */
\r
8202 cf.crTextColor = COLOR_ECHOOFF;
\r
8203 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8204 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8207 /* No Raw()...? */
\r
8209 void Colorize(ColorClass cc, int continuation)
\r
8211 currentColorClass = cc;
\r
8212 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8213 consoleCF.crTextColor = textAttribs[cc].color;
\r
8214 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8215 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8221 static char buf[MSG_SIZ];
\r
8222 DWORD bufsiz = MSG_SIZ;
\r
8224 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8225 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8227 if (!GetUserName(buf, &bufsiz)) {
\r
8228 /*DisplayError("Error getting user name", GetLastError());*/
\r
8229 strcpy(buf, "User");
\r
8237 static char buf[MSG_SIZ];
\r
8238 DWORD bufsiz = MSG_SIZ;
\r
8240 if (!GetComputerName(buf, &bufsiz)) {
\r
8241 /*DisplayError("Error getting host name", GetLastError());*/
\r
8242 strcpy(buf, "Unknown");
\r
8249 ClockTimerRunning()
\r
8251 return clockTimerEvent != 0;
\r
8257 if (clockTimerEvent == 0) return FALSE;
\r
8258 KillTimer(hwndMain, clockTimerEvent);
\r
8259 clockTimerEvent = 0;
\r
8264 StartClockTimer(long millisec)
\r
8266 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8267 (UINT) millisec, NULL);
\r
8271 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8274 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8276 if(appData.noGUI) return;
\r
8277 hdc = GetDC(hwndMain);
\r
8278 if (!IsIconic(hwndMain)) {
\r
8279 DisplayAClock(hdc, timeRemaining, highlight,
\r
8280 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8282 if (highlight && iconCurrent == iconBlack) {
\r
8283 iconCurrent = iconWhite;
\r
8284 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8285 if (IsIconic(hwndMain)) {
\r
8286 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8289 (void) ReleaseDC(hwndMain, hdc);
\r
8291 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8295 DisplayBlackClock(long timeRemaining, int highlight)
\r
8298 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8300 if(appData.noGUI) return;
\r
8301 hdc = GetDC(hwndMain);
\r
8302 if (!IsIconic(hwndMain)) {
\r
8303 DisplayAClock(hdc, timeRemaining, highlight,
\r
8304 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8306 if (highlight && iconCurrent == iconWhite) {
\r
8307 iconCurrent = iconBlack;
\r
8308 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8309 if (IsIconic(hwndMain)) {
\r
8310 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8313 (void) ReleaseDC(hwndMain, hdc);
\r
8315 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8320 LoadGameTimerRunning()
\r
8322 return loadGameTimerEvent != 0;
\r
8326 StopLoadGameTimer()
\r
8328 if (loadGameTimerEvent == 0) return FALSE;
\r
8329 KillTimer(hwndMain, loadGameTimerEvent);
\r
8330 loadGameTimerEvent = 0;
\r
8335 StartLoadGameTimer(long millisec)
\r
8337 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8338 (UINT) millisec, NULL);
\r
8346 char fileTitle[MSG_SIZ];
\r
8348 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8349 f = OpenFileDialog(hwndMain, "a", defName,
\r
8350 appData.oldSaveStyle ? "gam" : "pgn",
\r
8352 "Save Game to File", NULL, fileTitle, NULL);
\r
8354 SaveGame(f, 0, "");
\r
8361 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8363 if (delayedTimerEvent != 0) {
\r
8364 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8365 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8367 KillTimer(hwndMain, delayedTimerEvent);
\r
8368 delayedTimerEvent = 0;
\r
8369 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8370 delayedTimerCallback();
\r
8372 delayedTimerCallback = cb;
\r
8373 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8374 (UINT) millisec, NULL);
\r
8377 DelayedEventCallback
\r
8380 if (delayedTimerEvent) {
\r
8381 return delayedTimerCallback;
\r
8388 CancelDelayedEvent()
\r
8390 if (delayedTimerEvent) {
\r
8391 KillTimer(hwndMain, delayedTimerEvent);
\r
8392 delayedTimerEvent = 0;
\r
8396 DWORD GetWin32Priority(int nice)
\r
8397 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8399 REALTIME_PRIORITY_CLASS 0x00000100
\r
8400 HIGH_PRIORITY_CLASS 0x00000080
\r
8401 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8402 NORMAL_PRIORITY_CLASS 0x00000020
\r
8403 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8404 IDLE_PRIORITY_CLASS 0x00000040
\r
8406 if (nice < -15) return 0x00000080;
\r
8407 if (nice < 0) return 0x00008000;
\r
8408 if (nice == 0) return 0x00000020;
\r
8409 if (nice < 15) return 0x00004000;
\r
8410 return 0x00000040;
\r
8413 /* Start a child process running the given program.
\r
8414 The process's standard output can be read from "from", and its
\r
8415 standard input can be written to "to".
\r
8416 Exit with fatal error if anything goes wrong.
\r
8417 Returns an opaque pointer that can be used to destroy the process
\r
8421 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8423 #define BUFSIZE 4096
\r
8425 HANDLE hChildStdinRd, hChildStdinWr,
\r
8426 hChildStdoutRd, hChildStdoutWr;
\r
8427 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8428 SECURITY_ATTRIBUTES saAttr;
\r
8430 PROCESS_INFORMATION piProcInfo;
\r
8431 STARTUPINFO siStartInfo;
\r
8433 char buf[MSG_SIZ];
\r
8436 if (appData.debugMode) {
\r
8437 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8442 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8443 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8444 saAttr.bInheritHandle = TRUE;
\r
8445 saAttr.lpSecurityDescriptor = NULL;
\r
8448 * The steps for redirecting child's STDOUT:
\r
8449 * 1. Create anonymous pipe to be STDOUT for child.
\r
8450 * 2. Create a noninheritable duplicate of read handle,
\r
8451 * and close the inheritable read handle.
\r
8454 /* Create a pipe for the child's STDOUT. */
\r
8455 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8456 return GetLastError();
\r
8459 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8460 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8461 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8462 FALSE, /* not inherited */
\r
8463 DUPLICATE_SAME_ACCESS);
\r
8465 return GetLastError();
\r
8467 CloseHandle(hChildStdoutRd);
\r
8470 * The steps for redirecting child's STDIN:
\r
8471 * 1. Create anonymous pipe to be STDIN for child.
\r
8472 * 2. Create a noninheritable duplicate of write handle,
\r
8473 * and close the inheritable write handle.
\r
8476 /* Create a pipe for the child's STDIN. */
\r
8477 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8478 return GetLastError();
\r
8481 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8482 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8483 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8484 FALSE, /* not inherited */
\r
8485 DUPLICATE_SAME_ACCESS);
\r
8487 return GetLastError();
\r
8489 CloseHandle(hChildStdinWr);
\r
8491 /* Arrange to (1) look in dir for the child .exe file, and
\r
8492 * (2) have dir be the child's working directory. Interpret
\r
8493 * dir relative to the directory WinBoard loaded from. */
\r
8494 GetCurrentDirectory(MSG_SIZ, buf);
\r
8495 SetCurrentDirectory(installDir);
\r
8496 SetCurrentDirectory(dir);
\r
8498 /* Now create the child process. */
\r
8500 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8501 siStartInfo.lpReserved = NULL;
\r
8502 siStartInfo.lpDesktop = NULL;
\r
8503 siStartInfo.lpTitle = NULL;
\r
8504 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8505 siStartInfo.cbReserved2 = 0;
\r
8506 siStartInfo.lpReserved2 = NULL;
\r
8507 siStartInfo.hStdInput = hChildStdinRd;
\r
8508 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8509 siStartInfo.hStdError = hChildStdoutWr;
\r
8511 fSuccess = CreateProcess(NULL,
\r
8512 cmdLine, /* command line */
\r
8513 NULL, /* process security attributes */
\r
8514 NULL, /* primary thread security attrs */
\r
8515 TRUE, /* handles are inherited */
\r
8516 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8517 NULL, /* use parent's environment */
\r
8519 &siStartInfo, /* STARTUPINFO pointer */
\r
8520 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8522 err = GetLastError();
\r
8523 SetCurrentDirectory(buf); /* return to prev directory */
\r
8528 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8529 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8530 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8533 /* Close the handles we don't need in the parent */
\r
8534 CloseHandle(piProcInfo.hThread);
\r
8535 CloseHandle(hChildStdinRd);
\r
8536 CloseHandle(hChildStdoutWr);
\r
8538 /* Prepare return value */
\r
8539 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8540 cp->kind = CPReal;
\r
8541 cp->hProcess = piProcInfo.hProcess;
\r
8542 cp->pid = piProcInfo.dwProcessId;
\r
8543 cp->hFrom = hChildStdoutRdDup;
\r
8544 cp->hTo = hChildStdinWrDup;
\r
8546 *pr = (void *) cp;
\r
8548 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8549 2000 where engines sometimes don't see the initial command(s)
\r
8550 from WinBoard and hang. I don't understand how that can happen,
\r
8551 but the Sleep is harmless, so I've put it in. Others have also
\r
8552 reported what may be the same problem, so hopefully this will fix
\r
8553 it for them too. */
\r
8561 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8563 ChildProc *cp; int result;
\r
8565 cp = (ChildProc *) pr;
\r
8566 if (cp == NULL) return;
\r
8568 switch (cp->kind) {
\r
8570 /* TerminateProcess is considered harmful, so... */
\r
8571 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8572 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8573 /* The following doesn't work because the chess program
\r
8574 doesn't "have the same console" as WinBoard. Maybe
\r
8575 we could arrange for this even though neither WinBoard
\r
8576 nor the chess program uses a console for stdio? */
\r
8577 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8579 /* [AS] Special termination modes for misbehaving programs... */
\r
8580 if( signal == 9 ) {
\r
8581 result = TerminateProcess( cp->hProcess, 0 );
\r
8583 if ( appData.debugMode) {
\r
8584 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8587 else if( signal == 10 ) {
\r
8588 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8590 if( dw != WAIT_OBJECT_0 ) {
\r
8591 result = TerminateProcess( cp->hProcess, 0 );
\r
8593 if ( appData.debugMode) {
\r
8594 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8600 CloseHandle(cp->hProcess);
\r
8604 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8608 closesocket(cp->sock);
\r
8613 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8614 closesocket(cp->sock);
\r
8615 closesocket(cp->sock2);
\r
8623 InterruptChildProcess(ProcRef pr)
\r
8627 cp = (ChildProc *) pr;
\r
8628 if (cp == NULL) return;
\r
8629 switch (cp->kind) {
\r
8631 /* The following doesn't work because the chess program
\r
8632 doesn't "have the same console" as WinBoard. Maybe
\r
8633 we could arrange for this even though neither WinBoard
\r
8634 nor the chess program uses a console for stdio */
\r
8635 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8640 /* Can't interrupt */
\r
8644 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8651 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8653 char cmdLine[MSG_SIZ];
\r
8655 if (port[0] == NULLCHAR) {
\r
8656 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8658 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8660 return StartChildProcess(cmdLine, "", pr);
\r
8664 /* Code to open TCP sockets */
\r
8667 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8672 struct sockaddr_in sa, mysa;
\r
8673 struct hostent FAR *hp;
\r
8674 unsigned short uport;
\r
8675 WORD wVersionRequested;
\r
8678 /* Initialize socket DLL */
\r
8679 wVersionRequested = MAKEWORD(1, 1);
\r
8680 err = WSAStartup(wVersionRequested, &wsaData);
\r
8681 if (err != 0) return err;
\r
8684 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8685 err = WSAGetLastError();
\r
8690 /* Bind local address using (mostly) don't-care values.
\r
8692 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8693 mysa.sin_family = AF_INET;
\r
8694 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8695 uport = (unsigned short) 0;
\r
8696 mysa.sin_port = htons(uport);
\r
8697 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8698 == SOCKET_ERROR) {
\r
8699 err = WSAGetLastError();
\r
8704 /* Resolve remote host name */
\r
8705 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8706 if (!(hp = gethostbyname(host))) {
\r
8707 unsigned int b0, b1, b2, b3;
\r
8709 err = WSAGetLastError();
\r
8711 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8712 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8713 hp->h_addrtype = AF_INET;
\r
8715 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8716 hp->h_addr_list[0] = (char *) malloc(4);
\r
8717 hp->h_addr_list[0][0] = (char) b0;
\r
8718 hp->h_addr_list[0][1] = (char) b1;
\r
8719 hp->h_addr_list[0][2] = (char) b2;
\r
8720 hp->h_addr_list[0][3] = (char) b3;
\r
8726 sa.sin_family = hp->h_addrtype;
\r
8727 uport = (unsigned short) atoi(port);
\r
8728 sa.sin_port = htons(uport);
\r
8729 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8731 /* Make connection */
\r
8732 if (connect(s, (struct sockaddr *) &sa,
\r
8733 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8734 err = WSAGetLastError();
\r
8739 /* Prepare return value */
\r
8740 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8741 cp->kind = CPSock;
\r
8743 *pr = (ProcRef *) cp;
\r
8749 OpenCommPort(char *name, ProcRef *pr)
\r
8754 char fullname[MSG_SIZ];
\r
8756 if (*name != '\\')
\r
8757 sprintf(fullname, "\\\\.\\%s", name);
\r
8759 strcpy(fullname, name);
\r
8761 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8762 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8763 if (h == (HANDLE) -1) {
\r
8764 return GetLastError();
\r
8768 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8770 /* Accumulate characters until a 100ms pause, then parse */
\r
8771 ct.ReadIntervalTimeout = 100;
\r
8772 ct.ReadTotalTimeoutMultiplier = 0;
\r
8773 ct.ReadTotalTimeoutConstant = 0;
\r
8774 ct.WriteTotalTimeoutMultiplier = 0;
\r
8775 ct.WriteTotalTimeoutConstant = 0;
\r
8776 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8778 /* Prepare return value */
\r
8779 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8780 cp->kind = CPComm;
\r
8783 *pr = (ProcRef *) cp;
\r
8789 OpenLoopback(ProcRef *pr)
\r
8791 DisplayFatalError("Not implemented", 0, 1);
\r
8797 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8802 struct sockaddr_in sa, mysa;
\r
8803 struct hostent FAR *hp;
\r
8804 unsigned short uport;
\r
8805 WORD wVersionRequested;
\r
8808 char stderrPortStr[MSG_SIZ];
\r
8810 /* Initialize socket DLL */
\r
8811 wVersionRequested = MAKEWORD(1, 1);
\r
8812 err = WSAStartup(wVersionRequested, &wsaData);
\r
8813 if (err != 0) return err;
\r
8815 /* Resolve remote host name */
\r
8816 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8817 if (!(hp = gethostbyname(host))) {
\r
8818 unsigned int b0, b1, b2, b3;
\r
8820 err = WSAGetLastError();
\r
8822 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8823 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8824 hp->h_addrtype = AF_INET;
\r
8826 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8827 hp->h_addr_list[0] = (char *) malloc(4);
\r
8828 hp->h_addr_list[0][0] = (char) b0;
\r
8829 hp->h_addr_list[0][1] = (char) b1;
\r
8830 hp->h_addr_list[0][2] = (char) b2;
\r
8831 hp->h_addr_list[0][3] = (char) b3;
\r
8837 sa.sin_family = hp->h_addrtype;
\r
8838 uport = (unsigned short) 514;
\r
8839 sa.sin_port = htons(uport);
\r
8840 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8842 /* Bind local socket to unused "privileged" port address
\r
8844 s = INVALID_SOCKET;
\r
8845 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8846 mysa.sin_family = AF_INET;
\r
8847 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8848 for (fromPort = 1023;; fromPort--) {
\r
8849 if (fromPort < 0) {
\r
8851 return WSAEADDRINUSE;
\r
8853 if (s == INVALID_SOCKET) {
\r
8854 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8855 err = WSAGetLastError();
\r
8860 uport = (unsigned short) fromPort;
\r
8861 mysa.sin_port = htons(uport);
\r
8862 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8863 == SOCKET_ERROR) {
\r
8864 err = WSAGetLastError();
\r
8865 if (err == WSAEADDRINUSE) continue;
\r
8869 if (connect(s, (struct sockaddr *) &sa,
\r
8870 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8871 err = WSAGetLastError();
\r
8872 if (err == WSAEADDRINUSE) {
\r
8883 /* Bind stderr local socket to unused "privileged" port address
\r
8885 s2 = INVALID_SOCKET;
\r
8886 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8887 mysa.sin_family = AF_INET;
\r
8888 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8889 for (fromPort = 1023;; fromPort--) {
\r
8890 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8891 if (fromPort < 0) {
\r
8892 (void) closesocket(s);
\r
8894 return WSAEADDRINUSE;
\r
8896 if (s2 == INVALID_SOCKET) {
\r
8897 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8898 err = WSAGetLastError();
\r
8904 uport = (unsigned short) fromPort;
\r
8905 mysa.sin_port = htons(uport);
\r
8906 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8907 == SOCKET_ERROR) {
\r
8908 err = WSAGetLastError();
\r
8909 if (err == WSAEADDRINUSE) continue;
\r
8910 (void) closesocket(s);
\r
8914 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8915 err = WSAGetLastError();
\r
8916 if (err == WSAEADDRINUSE) {
\r
8918 s2 = INVALID_SOCKET;
\r
8921 (void) closesocket(s);
\r
8922 (void) closesocket(s2);
\r
8928 prevStderrPort = fromPort; // remember port used
\r
8929 sprintf(stderrPortStr, "%d", fromPort);
\r
8931 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8932 err = WSAGetLastError();
\r
8933 (void) closesocket(s);
\r
8934 (void) closesocket(s2);
\r
8939 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8940 err = WSAGetLastError();
\r
8941 (void) closesocket(s);
\r
8942 (void) closesocket(s2);
\r
8946 if (*user == NULLCHAR) user = UserName();
\r
8947 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8948 err = WSAGetLastError();
\r
8949 (void) closesocket(s);
\r
8950 (void) closesocket(s2);
\r
8954 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8955 err = WSAGetLastError();
\r
8956 (void) closesocket(s);
\r
8957 (void) closesocket(s2);
\r
8962 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8963 err = WSAGetLastError();
\r
8964 (void) closesocket(s);
\r
8965 (void) closesocket(s2);
\r
8969 (void) closesocket(s2); /* Stop listening */
\r
8971 /* Prepare return value */
\r
8972 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8973 cp->kind = CPRcmd;
\r
8976 *pr = (ProcRef *) cp;
\r
8983 AddInputSource(ProcRef pr, int lineByLine,
\r
8984 InputCallback func, VOIDSTAR closure)
\r
8986 InputSource *is, *is2 = NULL;
\r
8987 ChildProc *cp = (ChildProc *) pr;
\r
8989 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8990 is->lineByLine = lineByLine;
\r
8992 is->closure = closure;
\r
8993 is->second = NULL;
\r
8994 is->next = is->buf;
\r
8995 if (pr == NoProc) {
\r
8996 is->kind = CPReal;
\r
8997 consoleInputSource = is;
\r
8999 is->kind = cp->kind;
\r
9001 [AS] Try to avoid a race condition if the thread is given control too early:
\r
9002 we create all threads suspended so that the is->hThread variable can be
\r
9003 safely assigned, then let the threads start with ResumeThread.
\r
9005 switch (cp->kind) {
\r
9007 is->hFile = cp->hFrom;
\r
9008 cp->hFrom = NULL; /* now owned by InputThread */
\r
9010 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9011 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9015 is->hFile = cp->hFrom;
\r
9016 cp->hFrom = NULL; /* now owned by InputThread */
\r
9018 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9019 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9023 is->sock = cp->sock;
\r
9025 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9026 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9030 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9032 is->sock = cp->sock;
\r
9034 is2->sock = cp->sock2;
\r
9035 is2->second = is2;
\r
9037 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9038 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9040 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9041 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9045 if( is->hThread != NULL ) {
\r
9046 ResumeThread( is->hThread );
\r
9049 if( is2 != NULL && is2->hThread != NULL ) {
\r
9050 ResumeThread( is2->hThread );
\r
9054 return (InputSourceRef) is;
\r
9058 RemoveInputSource(InputSourceRef isr)
\r
9062 is = (InputSource *) isr;
\r
9063 is->hThread = NULL; /* tell thread to stop */
\r
9064 CloseHandle(is->hThread);
\r
9065 if (is->second != NULL) {
\r
9066 is->second->hThread = NULL;
\r
9067 CloseHandle(is->second->hThread);
\r
9071 int no_wrap(char *message, int count)
\r
9073 ConsoleOutput(message, count, FALSE);
\r
9078 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9081 int outCount = SOCKET_ERROR;
\r
9082 ChildProc *cp = (ChildProc *) pr;
\r
9083 static OVERLAPPED ovl;
\r
9084 static int line = 0;
\r
9088 if (appData.noJoin || !appData.useInternalWrap)
\r
9089 return no_wrap(message, count);
\r
9092 int width = get_term_width();
\r
9093 int len = wrap(NULL, message, count, width, &line);
\r
9094 char *msg = malloc(len);
\r
9098 return no_wrap(message, count);
\r
9101 dbgchk = wrap(msg, message, count, width, &line);
\r
9102 if (dbgchk != len && appData.debugMode)
\r
9103 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9104 ConsoleOutput(msg, len, FALSE);
\r
9111 if (ovl.hEvent == NULL) {
\r
9112 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9114 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9116 switch (cp->kind) {
\r
9119 outCount = send(cp->sock, message, count, 0);
\r
9120 if (outCount == SOCKET_ERROR) {
\r
9121 *outError = WSAGetLastError();
\r
9123 *outError = NO_ERROR;
\r
9128 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9129 &dOutCount, NULL)) {
\r
9130 *outError = NO_ERROR;
\r
9131 outCount = (int) dOutCount;
\r
9133 *outError = GetLastError();
\r
9138 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9139 &dOutCount, &ovl);
\r
9140 if (*outError == NO_ERROR) {
\r
9141 outCount = (int) dOutCount;
\r
9149 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9152 /* Ignore delay, not implemented for WinBoard */
\r
9153 return OutputToProcess(pr, message, count, outError);
\r
9158 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9159 char *buf, int count, int error)
\r
9161 DisplayFatalError("Not implemented", 0, 1);
\r
9164 /* see wgamelist.c for Game List functions */
\r
9165 /* see wedittags.c for Edit Tags functions */
\r
9172 char buf[MSG_SIZ];
\r
9175 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9176 f = fopen(buf, "r");
\r
9178 ProcessICSInitScript(f);
\r
9186 StartAnalysisClock()
\r
9188 if (analysisTimerEvent) return;
\r
9189 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9190 (UINT) 2000, NULL);
\r
9194 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9196 highlightInfo.sq[0].x = fromX;
\r
9197 highlightInfo.sq[0].y = fromY;
\r
9198 highlightInfo.sq[1].x = toX;
\r
9199 highlightInfo.sq[1].y = toY;
\r
9205 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9206 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9210 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9212 premoveHighlightInfo.sq[0].x = fromX;
\r
9213 premoveHighlightInfo.sq[0].y = fromY;
\r
9214 premoveHighlightInfo.sq[1].x = toX;
\r
9215 premoveHighlightInfo.sq[1].y = toY;
\r
9219 ClearPremoveHighlights()
\r
9221 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9222 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9226 ShutDownFrontEnd()
\r
9228 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9229 DeleteClipboardTempFiles();
\r
9235 if (IsIconic(hwndMain))
\r
9236 ShowWindow(hwndMain, SW_RESTORE);
\r
9238 SetActiveWindow(hwndMain);
\r
9242 * Prototypes for animation support routines
\r
9244 static void ScreenSquare(int column, int row, POINT * pt);
\r
9245 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9246 POINT frames[], int * nFrames);
\r
9250 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9251 { // [HGM] atomic: animate blast wave
\r
9253 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9254 explodeInfo.fromX = fromX;
\r
9255 explodeInfo.fromY = fromY;
\r
9256 explodeInfo.toX = toX;
\r
9257 explodeInfo.toY = toY;
\r
9258 for(i=1; i<nFrames; i++) {
\r
9259 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9260 DrawPosition(FALSE, NULL);
\r
9261 Sleep(appData.animSpeed);
\r
9263 explodeInfo.radius = 0;
\r
9264 DrawPosition(TRUE, NULL);
\r
9270 AnimateMove(board, fromX, fromY, toX, toY)
\r
9277 ChessSquare piece;
\r
9278 POINT start, finish, mid;
\r
9279 POINT frames[kFactor * 2 + 1];
\r
9282 if (!appData.animate) return;
\r
9283 if (doingSizing) return;
\r
9284 if (fromY < 0 || fromX < 0) return;
\r
9285 piece = board[fromY][fromX];
\r
9286 if (piece >= EmptySquare) return;
\r
9288 ScreenSquare(fromX, fromY, &start);
\r
9289 ScreenSquare(toX, toY, &finish);
\r
9291 /* All pieces except knights move in straight line */
\r
9292 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9293 mid.x = start.x + (finish.x - start.x) / 2;
\r
9294 mid.y = start.y + (finish.y - start.y) / 2;
\r
9296 /* Knight: make diagonal movement then straight */
\r
9297 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9298 mid.x = start.x + (finish.x - start.x) / 2;
\r
9302 mid.y = start.y + (finish.y - start.y) / 2;
\r
9306 /* Don't use as many frames for very short moves */
\r
9307 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9308 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9310 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9312 animInfo.from.x = fromX;
\r
9313 animInfo.from.y = fromY;
\r
9314 animInfo.to.x = toX;
\r
9315 animInfo.to.y = toY;
\r
9316 animInfo.lastpos = start;
\r
9317 animInfo.piece = piece;
\r
9318 for (n = 0; n < nFrames; n++) {
\r
9319 animInfo.pos = frames[n];
\r
9320 DrawPosition(FALSE, NULL);
\r
9321 animInfo.lastpos = animInfo.pos;
\r
9322 Sleep(appData.animSpeed);
\r
9324 animInfo.pos = finish;
\r
9325 DrawPosition(FALSE, NULL);
\r
9326 animInfo.piece = EmptySquare;
\r
9327 if(gameInfo.variant == VariantAtomic &&
\r
9328 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9329 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9332 /* Convert board position to corner of screen rect and color */
\r
9335 ScreenSquare(column, row, pt)
\r
9336 int column; int row; POINT * pt;
\r
9339 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9340 pt->y = lineGap + row * (squareSize + lineGap);
\r
9342 pt->x = lineGap + column * (squareSize + lineGap);
\r
9343 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9347 /* Generate a series of frame coords from start->mid->finish.
\r
9348 The movement rate doubles until the half way point is
\r
9349 reached, then halves back down to the final destination,
\r
9350 which gives a nice slow in/out effect. The algorithmn
\r
9351 may seem to generate too many intermediates for short
\r
9352 moves, but remember that the purpose is to attract the
\r
9353 viewers attention to the piece about to be moved and
\r
9354 then to where it ends up. Too few frames would be less
\r
9358 Tween(start, mid, finish, factor, frames, nFrames)
\r
9359 POINT * start; POINT * mid;
\r
9360 POINT * finish; int factor;
\r
9361 POINT frames[]; int * nFrames;
\r
9363 int n, fraction = 1, count = 0;
\r
9365 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9366 for (n = 0; n < factor; n++)
\r
9368 for (n = 0; n < factor; n++) {
\r
9369 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9370 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9372 fraction = fraction / 2;
\r
9376 frames[count] = *mid;
\r
9379 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9381 for (n = 0; n < factor; n++) {
\r
9382 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9383 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9385 fraction = fraction * 2;
\r
9391 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9393 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9395 EvalGraphSet( first, last, current, pvInfoList );
\r