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
1911 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1913 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1914 ChessSquare piece;
\r
1915 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1917 SIZE clockSize, messageSize;
\r
1919 char buf[MSG_SIZ];
\r
1921 HMENU hmenu = GetMenu(hwndMain);
\r
1922 RECT crect, wrect, oldRect;
\r
1924 LOGBRUSH logbrush;
\r
1926 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1927 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1929 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1930 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1932 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1933 oldRect.top = wpMain.y;
\r
1934 oldRect.right = wpMain.x + wpMain.width;
\r
1935 oldRect.bottom = wpMain.y + wpMain.height;
\r
1937 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1938 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1939 squareSize = sizeInfo[boardSize].squareSize;
\r
1940 lineGap = sizeInfo[boardSize].lineGap;
\r
1941 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1943 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1944 lineGap = appData.overrideLineGap;
\r
1947 if (tinyLayout != oldTinyLayout) {
\r
1948 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1950 style &= ~WS_SYSMENU;
\r
1951 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1952 "&Minimize\tCtrl+F4");
\r
1954 style |= WS_SYSMENU;
\r
1955 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1957 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1959 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1960 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1961 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1963 DrawMenuBar(hwndMain);
\r
1966 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1967 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1969 /* Get text area sizes */
\r
1970 hdc = GetDC(hwndMain);
\r
1971 if (appData.clockMode) {
\r
1972 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1974 sprintf(buf, "White");
\r
1976 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1977 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1978 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1979 str = "We only care about the height here";
\r
1980 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1981 SelectObject(hdc, oldFont);
\r
1982 ReleaseDC(hwndMain, hdc);
\r
1984 /* Compute where everything goes */
\r
1985 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1986 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1987 logoHeight = 2*clockSize.cy;
\r
1988 leftLogoRect.left = OUTER_MARGIN;
\r
1989 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1990 leftLogoRect.top = OUTER_MARGIN;
\r
1991 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1993 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1994 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1995 rightLogoRect.top = OUTER_MARGIN;
\r
1996 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1999 whiteRect.left = leftLogoRect.right;
\r
2000 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2001 whiteRect.top = OUTER_MARGIN;
\r
2002 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2004 blackRect.right = rightLogoRect.left;
\r
2005 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2006 blackRect.top = whiteRect.top;
\r
2007 blackRect.bottom = whiteRect.bottom;
\r
2009 whiteRect.left = OUTER_MARGIN;
\r
2010 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2011 whiteRect.top = OUTER_MARGIN;
\r
2012 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2014 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2015 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2016 blackRect.top = whiteRect.top;
\r
2017 blackRect.bottom = whiteRect.bottom;
\r
2019 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2022 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2023 if (appData.showButtonBar) {
\r
2024 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2025 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2027 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2029 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2030 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2032 boardRect.left = OUTER_MARGIN;
\r
2033 boardRect.right = boardRect.left + boardWidth;
\r
2034 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2035 boardRect.bottom = boardRect.top + boardHeight;
\r
2037 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2038 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2039 oldBoardSize = boardSize;
\r
2040 oldTinyLayout = tinyLayout;
\r
2041 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2042 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2043 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2044 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2045 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2046 wpMain.height = winH; // without disturbing window attachments
\r
2047 GetWindowRect(hwndMain, &wrect);
\r
2048 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2049 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2051 // [HGM] placement: let attached windows follow size change.
\r
2052 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2053 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2054 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2055 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2056 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2058 /* compensate if menu bar wrapped */
\r
2059 GetClientRect(hwndMain, &crect);
\r
2060 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2061 wpMain.height += offby;
\r
2063 case WMSZ_TOPLEFT:
\r
2064 SetWindowPos(hwndMain, NULL,
\r
2065 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2066 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2069 case WMSZ_TOPRIGHT:
\r
2071 SetWindowPos(hwndMain, NULL,
\r
2072 wrect.left, wrect.bottom - wpMain.height,
\r
2073 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2076 case WMSZ_BOTTOMLEFT:
\r
2078 SetWindowPos(hwndMain, NULL,
\r
2079 wrect.right - wpMain.width, wrect.top,
\r
2080 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2083 case WMSZ_BOTTOMRIGHT:
\r
2087 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2088 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2093 for (i = 0; i < N_BUTTONS; i++) {
\r
2094 if (buttonDesc[i].hwnd != NULL) {
\r
2095 DestroyWindow(buttonDesc[i].hwnd);
\r
2096 buttonDesc[i].hwnd = NULL;
\r
2098 if (appData.showButtonBar) {
\r
2099 buttonDesc[i].hwnd =
\r
2100 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2101 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2102 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2103 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2104 (HMENU) buttonDesc[i].id,
\r
2105 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2107 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2108 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2109 MAKELPARAM(FALSE, 0));
\r
2111 if (buttonDesc[i].id == IDM_Pause)
\r
2112 hwndPause = buttonDesc[i].hwnd;
\r
2113 buttonDesc[i].wndproc = (WNDPROC)
\r
2114 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2117 if (gridPen != NULL) DeleteObject(gridPen);
\r
2118 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2119 if (premovePen != NULL) DeleteObject(premovePen);
\r
2120 if (lineGap != 0) {
\r
2121 logbrush.lbStyle = BS_SOLID;
\r
2122 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2124 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2125 lineGap, &logbrush, 0, NULL);
\r
2126 logbrush.lbColor = highlightSquareColor;
\r
2128 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2129 lineGap, &logbrush, 0, NULL);
\r
2131 logbrush.lbColor = premoveHighlightColor;
\r
2133 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2134 lineGap, &logbrush, 0, NULL);
\r
2136 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2137 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2138 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2139 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2140 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2141 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2142 BOARD_WIDTH * (squareSize + lineGap);
\r
2143 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2145 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2146 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2147 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2148 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2149 lineGap / 2 + (i * (squareSize + lineGap));
\r
2150 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2151 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2152 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2156 /* [HGM] Licensing requirement */
\r
2158 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2161 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2163 GothicPopUp( "", VariantNormal);
\r
2166 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2168 /* Load piece bitmaps for this board size */
\r
2169 for (i=0; i<=2; i++) {
\r
2170 for (piece = WhitePawn;
\r
2171 (int) piece < (int) BlackPawn;
\r
2172 piece = (ChessSquare) ((int) piece + 1)) {
\r
2173 if (pieceBitmap[i][piece] != NULL)
\r
2174 DeleteObject(pieceBitmap[i][piece]);
\r
2178 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2179 // Orthodox Chess pieces
\r
2180 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2181 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2182 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2183 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2184 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2185 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2186 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2187 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2188 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2189 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2190 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2191 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2192 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2193 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2194 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2195 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2196 // in Shogi, Hijack the unused Queen for Lance
\r
2197 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2198 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2199 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2201 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2202 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2203 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2206 if(squareSize <= 72 && squareSize >= 33) {
\r
2207 /* A & C are available in most sizes now */
\r
2208 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2209 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2210 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2211 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2212 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2213 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2214 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2215 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2216 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2217 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2218 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2219 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2220 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2221 } else { // Smirf-like
\r
2222 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2223 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2224 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2226 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2227 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2228 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2229 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2230 } else { // WinBoard standard
\r
2231 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2232 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2233 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2238 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2239 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2240 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2241 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2242 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2243 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2244 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2245 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2246 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2247 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2248 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2249 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2250 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2251 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2252 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2253 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2254 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2255 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2256 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2257 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2258 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2259 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2260 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2261 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2262 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2263 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2264 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2265 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2266 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2267 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2268 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2270 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2271 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2272 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2273 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2274 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2275 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2276 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2277 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2278 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2279 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2280 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2281 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2282 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2284 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2285 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2286 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2287 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2288 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2289 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2290 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2291 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2292 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2293 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2294 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2295 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2298 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2299 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2300 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2301 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2302 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2303 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2304 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2305 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2306 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2307 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2308 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2309 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2310 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2311 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2312 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2316 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2317 /* special Shogi support in this size */
\r
2318 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2319 for (piece = WhitePawn;
\r
2320 (int) piece < (int) BlackPawn;
\r
2321 piece = (ChessSquare) ((int) piece + 1)) {
\r
2322 if (pieceBitmap[i][piece] != NULL)
\r
2323 DeleteObject(pieceBitmap[i][piece]);
\r
2326 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2327 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2328 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2330 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2331 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2332 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2333 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2334 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2335 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2336 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2337 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2340 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2341 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2342 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2344 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2345 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2346 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2347 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2348 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2349 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2350 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2351 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2354 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2355 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2356 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2358 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2359 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2360 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2361 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2362 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2363 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2364 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2365 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2373 PieceBitmap(ChessSquare p, int kind)
\r
2375 if ((int) p >= (int) BlackPawn)
\r
2376 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2378 return pieceBitmap[kind][(int) p];
\r
2381 /***************************************************************/
\r
2383 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2384 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2386 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2387 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2391 SquareToPos(int row, int column, int * x, int * y)
\r
2394 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2395 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2397 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2398 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2403 DrawCoordsOnDC(HDC hdc)
\r
2405 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
2406 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
2407 char str[2] = { NULLCHAR, NULLCHAR };
\r
2408 int oldMode, oldAlign, x, y, start, i;
\r
2412 if (!appData.showCoords)
\r
2415 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2417 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2418 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2419 oldAlign = GetTextAlign(hdc);
\r
2420 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2422 y = boardRect.top + lineGap;
\r
2423 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2425 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2426 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2427 str[0] = files[start + i];
\r
2428 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2429 y += squareSize + lineGap;
\r
2432 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2434 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2435 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2436 str[0] = ranks[start + i];
\r
2437 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2438 x += squareSize + lineGap;
\r
2441 SelectObject(hdc, oldBrush);
\r
2442 SetBkMode(hdc, oldMode);
\r
2443 SetTextAlign(hdc, oldAlign);
\r
2444 SelectObject(hdc, oldFont);
\r
2448 DrawGridOnDC(HDC hdc)
\r
2452 if (lineGap != 0) {
\r
2453 oldPen = SelectObject(hdc, gridPen);
\r
2454 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2455 SelectObject(hdc, oldPen);
\r
2459 #define HIGHLIGHT_PEN 0
\r
2460 #define PREMOVE_PEN 1
\r
2463 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2466 HPEN oldPen, hPen;
\r
2467 if (lineGap == 0) return;
\r
2469 x1 = boardRect.left +
\r
2470 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2471 y1 = boardRect.top +
\r
2472 lineGap/2 + y * (squareSize + lineGap);
\r
2474 x1 = boardRect.left +
\r
2475 lineGap/2 + x * (squareSize + lineGap);
\r
2476 y1 = boardRect.top +
\r
2477 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2479 hPen = pen ? premovePen : highlightPen;
\r
2480 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2481 MoveToEx(hdc, x1, y1, NULL);
\r
2482 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2483 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2484 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2485 LineTo(hdc, x1, y1);
\r
2486 SelectObject(hdc, oldPen);
\r
2490 DrawHighlightsOnDC(HDC hdc)
\r
2493 for (i=0; i<2; i++) {
\r
2494 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2495 DrawHighlightOnDC(hdc, TRUE,
\r
2496 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2499 for (i=0; i<2; i++) {
\r
2500 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2501 premoveHighlightInfo.sq[i].y >= 0) {
\r
2502 DrawHighlightOnDC(hdc, TRUE,
\r
2503 premoveHighlightInfo.sq[i].x,
\r
2504 premoveHighlightInfo.sq[i].y,
\r
2510 /* Note: sqcolor is used only in monoMode */
\r
2511 /* Note that this code is largely duplicated in woptions.c,
\r
2512 function DrawSampleSquare, so that needs to be updated too */
\r
2514 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2516 HBITMAP oldBitmap;
\r
2520 if (appData.blindfold) return;
\r
2522 /* [AS] Use font-based pieces if needed */
\r
2523 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2524 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2525 CreatePiecesFromFont();
\r
2527 if( fontBitmapSquareSize == squareSize ) {
\r
2528 int index = TranslatePieceToFontPiece(piece);
\r
2530 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2534 squareSize, squareSize,
\r
2539 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2543 squareSize, squareSize,
\r
2552 if (appData.monoMode) {
\r
2553 SelectObject(tmphdc, PieceBitmap(piece,
\r
2554 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2555 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2556 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2558 tmpSize = squareSize;
\r
2560 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2561 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2562 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2563 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2564 x += (squareSize - minorSize)>>1;
\r
2565 y += squareSize - minorSize - 2;
\r
2566 tmpSize = minorSize;
\r
2568 if (color || appData.allWhite ) {
\r
2569 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2571 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2572 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2573 if(appData.upsideDown && color==flipView)
\r
2574 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2576 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2577 /* Use black for outline of white pieces */
\r
2578 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2579 if(appData.upsideDown && color==flipView)
\r
2580 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2582 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2584 /* Use square color for details of black pieces */
\r
2585 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2586 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2587 if(appData.upsideDown && !flipView)
\r
2588 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2590 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2592 SelectObject(hdc, oldBrush);
\r
2593 SelectObject(tmphdc, oldBitmap);
\r
2597 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2598 int GetBackTextureMode( int algo )
\r
2600 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2604 case BACK_TEXTURE_MODE_PLAIN:
\r
2605 result = 1; /* Always use identity map */
\r
2607 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2608 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2616 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2617 to handle redraws cleanly (as random numbers would always be different).
\r
2619 VOID RebuildTextureSquareInfo()
\r
2629 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2631 if( liteBackTexture != NULL ) {
\r
2632 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2633 lite_w = bi.bmWidth;
\r
2634 lite_h = bi.bmHeight;
\r
2638 if( darkBackTexture != NULL ) {
\r
2639 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2640 dark_w = bi.bmWidth;
\r
2641 dark_h = bi.bmHeight;
\r
2645 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2646 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2647 if( (col + row) & 1 ) {
\r
2649 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2650 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2651 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2652 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2657 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2658 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2659 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2660 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2667 /* [AS] Arrow highlighting support */
\r
2669 static int A_WIDTH = 5; /* Width of arrow body */
\r
2671 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2672 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2674 static double Sqr( double x )
\r
2679 static int Round( double x )
\r
2681 return (int) (x + 0.5);
\r
2684 /* Draw an arrow between two points using current settings */
\r
2685 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2688 double dx, dy, j, k, x, y;
\r
2690 if( d_x == s_x ) {
\r
2691 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2693 arrow[0].x = s_x + A_WIDTH;
\r
2696 arrow[1].x = s_x + A_WIDTH;
\r
2697 arrow[1].y = d_y - h;
\r
2699 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2700 arrow[2].y = d_y - h;
\r
2705 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2706 arrow[4].y = d_y - h;
\r
2708 arrow[5].x = s_x - A_WIDTH;
\r
2709 arrow[5].y = d_y - h;
\r
2711 arrow[6].x = s_x - A_WIDTH;
\r
2714 else if( d_y == s_y ) {
\r
2715 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2718 arrow[0].y = s_y + A_WIDTH;
\r
2720 arrow[1].x = d_x - w;
\r
2721 arrow[1].y = s_y + A_WIDTH;
\r
2723 arrow[2].x = d_x - w;
\r
2724 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2729 arrow[4].x = d_x - w;
\r
2730 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2732 arrow[5].x = d_x - w;
\r
2733 arrow[5].y = s_y - A_WIDTH;
\r
2736 arrow[6].y = s_y - A_WIDTH;
\r
2739 /* [AS] Needed a lot of paper for this! :-) */
\r
2740 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2741 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2743 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2745 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2750 arrow[0].x = Round(x - j);
\r
2751 arrow[0].y = Round(y + j*dx);
\r
2753 arrow[1].x = Round(x + j);
\r
2754 arrow[1].y = Round(y - j*dx);
\r
2757 x = (double) d_x - k;
\r
2758 y = (double) d_y - k*dy;
\r
2761 x = (double) d_x + k;
\r
2762 y = (double) d_y + k*dy;
\r
2765 arrow[2].x = Round(x + j);
\r
2766 arrow[2].y = Round(y - j*dx);
\r
2768 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2769 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2774 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2775 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2777 arrow[6].x = Round(x - j);
\r
2778 arrow[6].y = Round(y + j*dx);
\r
2781 Polygon( hdc, arrow, 7 );
\r
2784 /* [AS] Draw an arrow between two squares */
\r
2785 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2787 int s_x, s_y, d_x, d_y;
\r
2794 if( s_col == d_col && s_row == d_row ) {
\r
2798 /* Get source and destination points */
\r
2799 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2800 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2803 d_y += squareSize / 4;
\r
2805 else if( d_y < s_y ) {
\r
2806 d_y += 3 * squareSize / 4;
\r
2809 d_y += squareSize / 2;
\r
2813 d_x += squareSize / 4;
\r
2815 else if( d_x < s_x ) {
\r
2816 d_x += 3 * squareSize / 4;
\r
2819 d_x += squareSize / 2;
\r
2822 s_x += squareSize / 2;
\r
2823 s_y += squareSize / 2;
\r
2825 /* Adjust width */
\r
2826 A_WIDTH = squareSize / 14;
\r
2829 stLB.lbStyle = BS_SOLID;
\r
2830 stLB.lbColor = appData.highlightArrowColor;
\r
2833 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2834 holdpen = SelectObject( hdc, hpen );
\r
2835 hbrush = CreateBrushIndirect( &stLB );
\r
2836 holdbrush = SelectObject( hdc, hbrush );
\r
2838 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2840 SelectObject( hdc, holdpen );
\r
2841 SelectObject( hdc, holdbrush );
\r
2842 DeleteObject( hpen );
\r
2843 DeleteObject( hbrush );
\r
2846 BOOL HasHighlightInfo()
\r
2848 BOOL result = FALSE;
\r
2850 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2851 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2859 BOOL IsDrawArrowEnabled()
\r
2861 BOOL result = FALSE;
\r
2863 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2870 VOID DrawArrowHighlight( HDC hdc )
\r
2872 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2873 DrawArrowBetweenSquares( hdc,
\r
2874 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2875 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2879 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2881 HRGN result = NULL;
\r
2883 if( HasHighlightInfo() ) {
\r
2884 int x1, y1, x2, y2;
\r
2885 int sx, sy, dx, dy;
\r
2887 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2888 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2890 sx = MIN( x1, x2 );
\r
2891 sy = MIN( y1, y2 );
\r
2892 dx = MAX( x1, x2 ) + squareSize;
\r
2893 dy = MAX( y1, y2 ) + squareSize;
\r
2895 result = CreateRectRgn( sx, sy, dx, dy );
\r
2902 Warning: this function modifies the behavior of several other functions.
\r
2904 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2905 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2906 repaint is scattered all over the place, which is not good for features such as
\r
2907 "arrow highlighting" that require a full repaint of the board.
\r
2909 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2910 user interaction, when speed is not so important) but especially to avoid errors
\r
2911 in the displayed graphics.
\r
2913 In such patched places, I always try refer to this function so there is a single
\r
2914 place to maintain knowledge.
\r
2916 To restore the original behavior, just return FALSE unconditionally.
\r
2918 BOOL IsFullRepaintPreferrable()
\r
2920 BOOL result = FALSE;
\r
2922 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2923 /* Arrow may appear on the board */
\r
2931 This function is called by DrawPosition to know whether a full repaint must
\r
2934 Only DrawPosition may directly call this function, which makes use of
\r
2935 some state information. Other function should call DrawPosition specifying
\r
2936 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2938 BOOL DrawPositionNeedsFullRepaint()
\r
2940 BOOL result = FALSE;
\r
2943 Probably a slightly better policy would be to trigger a full repaint
\r
2944 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2945 but animation is fast enough that it's difficult to notice.
\r
2947 if( animInfo.piece == EmptySquare ) {
\r
2948 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2957 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2959 int row, column, x, y, square_color, piece_color;
\r
2960 ChessSquare piece;
\r
2962 HDC texture_hdc = NULL;
\r
2964 /* [AS] Initialize background textures if needed */
\r
2965 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2966 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2967 if( backTextureSquareSize != squareSize
\r
2968 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2969 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2970 backTextureSquareSize = squareSize;
\r
2971 RebuildTextureSquareInfo();
\r
2974 texture_hdc = CreateCompatibleDC( hdc );
\r
2977 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2978 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2980 SquareToPos(row, column, &x, &y);
\r
2982 piece = board[row][column];
\r
2984 square_color = ((column + row) % 2) == 1;
\r
2985 if( gameInfo.variant == VariantXiangqi ) {
\r
2986 square_color = !InPalace(row, column);
\r
2987 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2988 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2990 piece_color = (int) piece < (int) BlackPawn;
\r
2993 /* [HGM] holdings file: light square or black */
\r
2994 if(column == BOARD_LEFT-2) {
\r
2995 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2998 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3002 if(column == BOARD_RGHT + 1 ) {
\r
3003 if( row < gameInfo.holdingsSize )
\r
3006 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3010 if(column == BOARD_LEFT-1 ) /* left align */
\r
3011 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3012 else if( column == BOARD_RGHT) /* right align */
\r
3013 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3015 if (appData.monoMode) {
\r
3016 if (piece == EmptySquare) {
\r
3017 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3018 square_color ? WHITENESS : BLACKNESS);
\r
3020 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3023 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3024 /* [AS] Draw the square using a texture bitmap */
\r
3025 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3026 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3027 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3030 squareSize, squareSize,
\r
3033 backTextureSquareInfo[r][c].mode,
\r
3034 backTextureSquareInfo[r][c].x,
\r
3035 backTextureSquareInfo[r][c].y );
\r
3037 SelectObject( texture_hdc, hbm );
\r
3039 if (piece != EmptySquare) {
\r
3040 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3044 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3046 oldBrush = SelectObject(hdc, brush );
\r
3047 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3048 SelectObject(hdc, oldBrush);
\r
3049 if (piece != EmptySquare)
\r
3050 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3055 if( texture_hdc != NULL ) {
\r
3056 DeleteDC( texture_hdc );
\r
3060 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3061 void fputDW(FILE *f, int x)
\r
3063 fputc(x & 255, f);
\r
3064 fputc(x>>8 & 255, f);
\r
3065 fputc(x>>16 & 255, f);
\r
3066 fputc(x>>24 & 255, f);
\r
3069 #define MAX_CLIPS 200 /* more than enough */
\r
3072 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3074 // HBITMAP bufferBitmap;
\r
3079 int w = 100, h = 50;
\r
3081 if(logo == NULL) return;
\r
3082 // GetClientRect(hwndMain, &Rect);
\r
3083 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3084 // Rect.bottom-Rect.top+1);
\r
3085 tmphdc = CreateCompatibleDC(hdc);
\r
3086 hbm = SelectObject(tmphdc, logo);
\r
3087 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3091 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3092 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3093 SelectObject(tmphdc, hbm);
\r
3097 static HDC hdcSeek;
\r
3099 // [HGM] seekgraph
\r
3100 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3103 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3104 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3105 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3106 SelectObject( hdcSeek, hp );
\r
3109 // front-end wrapper for drawing functions to do rectangles
\r
3110 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3115 if (hdcSeek == NULL) {
\r
3116 hdcSeek = GetDC(hwndMain);
\r
3117 if (!appData.monoMode) {
\r
3118 SelectPalette(hdcSeek, hPal, FALSE);
\r
3119 RealizePalette(hdcSeek);
\r
3122 hp = SelectObject( hdcSeek, gridPen );
\r
3123 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3124 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3125 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3126 SelectObject( hdcSeek, hp );
\r
3129 // front-end wrapper for putting text in graph
\r
3130 void DrawSeekText(char *buf, int x, int y)
\r
3133 SetBkMode( hdcSeek, TRANSPARENT );
\r
3134 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3135 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3138 void DrawSeekDot(int x, int y, int color)
\r
3140 int square = color & 0x80;
\r
3142 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3143 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3145 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3146 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3148 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3149 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3150 SelectObject(hdcSeek, oldBrush);
\r
3154 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3156 static Board lastReq, lastDrawn;
\r
3157 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3158 static int lastDrawnFlipView = 0;
\r
3159 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3160 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3163 HBITMAP bufferBitmap;
\r
3164 HBITMAP oldBitmap;
\r
3166 HRGN clips[MAX_CLIPS];
\r
3167 ChessSquare dragged_piece = EmptySquare;
\r
3169 /* I'm undecided on this - this function figures out whether a full
\r
3170 * repaint is necessary on its own, so there's no real reason to have the
\r
3171 * caller tell it that. I think this can safely be set to FALSE - but
\r
3172 * if we trust the callers not to request full repaints unnessesarily, then
\r
3173 * we could skip some clipping work. In other words, only request a full
\r
3174 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3175 * gamestart and similar) --Hawk
\r
3177 Boolean fullrepaint = repaint;
\r
3179 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3181 if( DrawPositionNeedsFullRepaint() ) {
\r
3182 fullrepaint = TRUE;
\r
3185 if (board == NULL) {
\r
3186 if (!lastReqValid) {
\r
3191 CopyBoard(lastReq, board);
\r
3195 if (doingSizing) {
\r
3199 if (IsIconic(hwndMain)) {
\r
3203 if (hdc == NULL) {
\r
3204 hdc = GetDC(hwndMain);
\r
3205 if (!appData.monoMode) {
\r
3206 SelectPalette(hdc, hPal, FALSE);
\r
3207 RealizePalette(hdc);
\r
3211 releaseDC = FALSE;
\r
3214 /* Create some work-DCs */
\r
3215 hdcmem = CreateCompatibleDC(hdc);
\r
3216 tmphdc = CreateCompatibleDC(hdc);
\r
3218 /* If dragging is in progress, we temporarely remove the piece */
\r
3219 /* [HGM] or temporarily decrease count if stacked */
\r
3220 /* !! Moved to before board compare !! */
\r
3221 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3222 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3223 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3224 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3225 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3227 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3228 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3229 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3231 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3234 /* Figure out which squares need updating by comparing the
\r
3235 * newest board with the last drawn board and checking if
\r
3236 * flipping has changed.
\r
3238 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3239 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3240 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3241 if (lastDrawn[row][column] != board[row][column]) {
\r
3242 SquareToPos(row, column, &x, &y);
\r
3243 clips[num_clips++] =
\r
3244 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3248 for (i=0; i<2; i++) {
\r
3249 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3250 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3251 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3252 lastDrawnHighlight.sq[i].y >= 0) {
\r
3253 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3254 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3255 clips[num_clips++] =
\r
3256 CreateRectRgn(x - lineGap, y - lineGap,
\r
3257 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3259 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3260 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3261 clips[num_clips++] =
\r
3262 CreateRectRgn(x - lineGap, y - lineGap,
\r
3263 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3267 for (i=0; i<2; i++) {
\r
3268 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3269 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3270 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3271 lastDrawnPremove.sq[i].y >= 0) {
\r
3272 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3273 lastDrawnPremove.sq[i].x, &x, &y);
\r
3274 clips[num_clips++] =
\r
3275 CreateRectRgn(x - lineGap, y - lineGap,
\r
3276 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3278 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3279 premoveHighlightInfo.sq[i].y >= 0) {
\r
3280 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3281 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3282 clips[num_clips++] =
\r
3283 CreateRectRgn(x - lineGap, y - lineGap,
\r
3284 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3289 fullrepaint = TRUE;
\r
3292 /* Create a buffer bitmap - this is the actual bitmap
\r
3293 * being written to. When all the work is done, we can
\r
3294 * copy it to the real DC (the screen). This avoids
\r
3295 * the problems with flickering.
\r
3297 GetClientRect(hwndMain, &Rect);
\r
3298 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3299 Rect.bottom-Rect.top+1);
\r
3300 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3301 if (!appData.monoMode) {
\r
3302 SelectPalette(hdcmem, hPal, FALSE);
\r
3305 /* Create clips for dragging */
\r
3306 if (!fullrepaint) {
\r
3307 if (dragInfo.from.x >= 0) {
\r
3308 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3309 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3311 if (dragInfo.start.x >= 0) {
\r
3312 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3313 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3315 if (dragInfo.pos.x >= 0) {
\r
3316 x = dragInfo.pos.x - squareSize / 2;
\r
3317 y = dragInfo.pos.y - squareSize / 2;
\r
3318 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3320 if (dragInfo.lastpos.x >= 0) {
\r
3321 x = dragInfo.lastpos.x - squareSize / 2;
\r
3322 y = dragInfo.lastpos.y - squareSize / 2;
\r
3323 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3327 /* Are we animating a move?
\r
3329 * - remove the piece from the board (temporarely)
\r
3330 * - calculate the clipping region
\r
3332 if (!fullrepaint) {
\r
3333 if (animInfo.piece != EmptySquare) {
\r
3334 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3335 x = boardRect.left + animInfo.lastpos.x;
\r
3336 y = boardRect.top + animInfo.lastpos.y;
\r
3337 x2 = boardRect.left + animInfo.pos.x;
\r
3338 y2 = boardRect.top + animInfo.pos.y;
\r
3339 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3340 /* Slight kludge. The real problem is that after AnimateMove is
\r
3341 done, the position on the screen does not match lastDrawn.
\r
3342 This currently causes trouble only on e.p. captures in
\r
3343 atomic, where the piece moves to an empty square and then
\r
3344 explodes. The old and new positions both had an empty square
\r
3345 at the destination, but animation has drawn a piece there and
\r
3346 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3347 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3351 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3352 if (num_clips == 0)
\r
3353 fullrepaint = TRUE;
\r
3355 /* Set clipping on the memory DC */
\r
3356 if (!fullrepaint) {
\r
3357 SelectClipRgn(hdcmem, clips[0]);
\r
3358 for (x = 1; x < num_clips; x++) {
\r
3359 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3360 abort(); // this should never ever happen!
\r
3364 /* Do all the drawing to the memory DC */
\r
3365 if(explodeInfo.radius) { // [HGM] atomic
\r
3367 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3368 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3369 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3370 x += squareSize/2;
\r
3371 y += squareSize/2;
\r
3372 if(!fullrepaint) {
\r
3373 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3374 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3376 DrawGridOnDC(hdcmem);
\r
3377 DrawHighlightsOnDC(hdcmem);
\r
3378 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3379 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3380 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3381 SelectObject(hdcmem, oldBrush);
\r
3383 DrawGridOnDC(hdcmem);
\r
3384 DrawHighlightsOnDC(hdcmem);
\r
3385 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3387 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3388 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3389 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3390 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3391 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3392 SquareToPos(row, column, &x, &y);
\r
3393 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3394 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3395 SelectObject(hdcmem, oldBrush);
\r
3400 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3401 if(appData.autoLogo) {
\r
3403 switch(gameMode) { // pick logos based on game mode
\r
3404 case IcsObserving:
\r
3405 whiteLogo = second.programLogo; // ICS logo
\r
3406 blackLogo = second.programLogo;
\r
3409 case IcsPlayingWhite:
\r
3410 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3411 blackLogo = second.programLogo; // ICS logo
\r
3413 case IcsPlayingBlack:
\r
3414 whiteLogo = second.programLogo; // ICS logo
\r
3415 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3417 case TwoMachinesPlay:
\r
3418 if(first.twoMachinesColor[0] == 'b') {
\r
3419 whiteLogo = second.programLogo;
\r
3420 blackLogo = first.programLogo;
\r
3423 case MachinePlaysWhite:
\r
3424 blackLogo = userLogo;
\r
3426 case MachinePlaysBlack:
\r
3427 whiteLogo = userLogo;
\r
3428 blackLogo = first.programLogo;
\r
3431 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3432 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3435 if( appData.highlightMoveWithArrow ) {
\r
3436 DrawArrowHighlight(hdcmem);
\r
3439 DrawCoordsOnDC(hdcmem);
\r
3441 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3442 /* to make sure lastDrawn contains what is actually drawn */
\r
3444 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3445 if (dragged_piece != EmptySquare) {
\r
3446 /* [HGM] or restack */
\r
3447 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3448 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3450 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3451 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3452 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3453 x = dragInfo.pos.x - squareSize / 2;
\r
3454 y = dragInfo.pos.y - squareSize / 2;
\r
3455 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3456 ((int) dragged_piece < (int) BlackPawn),
\r
3457 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3460 /* Put the animated piece back into place and draw it */
\r
3461 if (animInfo.piece != EmptySquare) {
\r
3462 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3463 x = boardRect.left + animInfo.pos.x;
\r
3464 y = boardRect.top + animInfo.pos.y;
\r
3465 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3466 ((int) animInfo.piece < (int) BlackPawn),
\r
3467 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3470 /* Release the bufferBitmap by selecting in the old bitmap
\r
3471 * and delete the memory DC
\r
3473 SelectObject(hdcmem, oldBitmap);
\r
3476 /* Set clipping on the target DC */
\r
3477 if (!fullrepaint) {
\r
3478 SelectClipRgn(hdc, clips[0]);
\r
3479 for (x = 1; x < num_clips; x++) {
\r
3480 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3481 abort(); // this should never ever happen!
\r
3485 /* Copy the new bitmap onto the screen in one go.
\r
3486 * This way we avoid any flickering
\r
3488 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3489 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3490 boardRect.right - boardRect.left,
\r
3491 boardRect.bottom - boardRect.top,
\r
3492 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3493 if(saveDiagFlag) {
\r
3494 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3495 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3497 GetObject(bufferBitmap, sizeof(b), &b);
\r
3498 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3499 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3500 bih.biWidth = b.bmWidth;
\r
3501 bih.biHeight = b.bmHeight;
\r
3503 bih.biBitCount = b.bmBitsPixel;
\r
3504 bih.biCompression = 0;
\r
3505 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3506 bih.biXPelsPerMeter = 0;
\r
3507 bih.biYPelsPerMeter = 0;
\r
3508 bih.biClrUsed = 0;
\r
3509 bih.biClrImportant = 0;
\r
3510 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3511 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3512 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3513 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3515 wb = b.bmWidthBytes;
\r
3517 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3518 int k = ((int*) pData)[i];
\r
3519 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3520 if(j >= 16) break;
\r
3522 if(j >= nrColors) nrColors = j+1;
\r
3524 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3526 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3527 for(w=0; w<(wb>>2); w+=2) {
\r
3528 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3529 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3530 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3531 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3532 pData[p++] = m | j<<4;
\r
3534 while(p&3) pData[p++] = 0;
\r
3537 wb = ((wb+31)>>5)<<2;
\r
3539 // write BITMAPFILEHEADER
\r
3540 fprintf(diagFile, "BM");
\r
3541 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3542 fputDW(diagFile, 0);
\r
3543 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3544 // write BITMAPINFOHEADER
\r
3545 fputDW(diagFile, 40);
\r
3546 fputDW(diagFile, b.bmWidth);
\r
3547 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3548 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3549 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\r
3550 fputDW(diagFile, 0);
\r
3551 fputDW(diagFile, 0);
\r
3552 fputDW(diagFile, 0);
\r
3553 fputDW(diagFile, 0);
\r
3554 fputDW(diagFile, 0);
\r
3555 fputDW(diagFile, 0);
\r
3556 // write color table
\r
3558 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3559 // write bitmap data
\r
3560 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3561 fputc(pData[i], diagFile);
\r
3565 SelectObject(tmphdc, oldBitmap);
\r
3567 /* Massive cleanup */
\r
3568 for (x = 0; x < num_clips; x++)
\r
3569 DeleteObject(clips[x]);
\r
3572 DeleteObject(bufferBitmap);
\r
3575 ReleaseDC(hwndMain, hdc);
\r
3577 if (lastDrawnFlipView != flipView) {
\r
3579 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3581 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3584 /* CopyBoard(lastDrawn, board);*/
\r
3585 lastDrawnHighlight = highlightInfo;
\r
3586 lastDrawnPremove = premoveHighlightInfo;
\r
3587 lastDrawnFlipView = flipView;
\r
3588 lastDrawnValid = 1;
\r
3591 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3596 saveDiagFlag = 1; diagFile = f;
\r
3597 HDCDrawPosition(NULL, TRUE, NULL);
\r
3601 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3608 /*---------------------------------------------------------------------------*\
\r
3609 | CLIENT PAINT PROCEDURE
\r
3610 | This is the main event-handler for the WM_PAINT message.
\r
3612 \*---------------------------------------------------------------------------*/
\r
3614 PaintProc(HWND hwnd)
\r
3620 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3621 if (IsIconic(hwnd)) {
\r
3622 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3624 if (!appData.monoMode) {
\r
3625 SelectPalette(hdc, hPal, FALSE);
\r
3626 RealizePalette(hdc);
\r
3628 HDCDrawPosition(hdc, 1, NULL);
\r
3630 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3631 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3632 ETO_CLIPPED|ETO_OPAQUE,
\r
3633 &messageRect, messageText, strlen(messageText), NULL);
\r
3634 SelectObject(hdc, oldFont);
\r
3635 DisplayBothClocks();
\r
3637 EndPaint(hwnd,&ps);
\r
3645 * If the user selects on a border boundary, return -1; if off the board,
\r
3646 * return -2. Otherwise map the event coordinate to the square.
\r
3647 * The offset boardRect.left or boardRect.top must already have been
\r
3648 * subtracted from x.
\r
3650 int EventToSquare(x, limit)
\r
3658 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3660 x /= (squareSize + lineGap);
\r
3672 DropEnable dropEnables[] = {
\r
3673 { 'P', DP_Pawn, "Pawn" },
\r
3674 { 'N', DP_Knight, "Knight" },
\r
3675 { 'B', DP_Bishop, "Bishop" },
\r
3676 { 'R', DP_Rook, "Rook" },
\r
3677 { 'Q', DP_Queen, "Queen" },
\r
3681 SetupDropMenu(HMENU hmenu)
\r
3683 int i, count, enable;
\r
3685 extern char white_holding[], black_holding[];
\r
3686 char item[MSG_SIZ];
\r
3688 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3689 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3690 dropEnables[i].piece);
\r
3692 while (p && *p++ == dropEnables[i].piece) count++;
\r
3693 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3694 enable = count > 0 || !appData.testLegality
\r
3695 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3696 && !appData.icsActive);
\r
3697 ModifyMenu(hmenu, dropEnables[i].command,
\r
3698 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3699 dropEnables[i].command, item);
\r
3703 void DragPieceBegin(int x, int y)
\r
3705 dragInfo.lastpos.x = boardRect.left + x;
\r
3706 dragInfo.lastpos.y = boardRect.top + y;
\r
3707 dragInfo.from.x = fromX;
\r
3708 dragInfo.from.y = fromY;
\r
3709 dragInfo.start = dragInfo.from;
\r
3710 SetCapture(hwndMain);
\r
3713 void DragPieceEnd(int x, int y)
\r
3716 dragInfo.start.x = dragInfo.start.y = -1;
\r
3717 dragInfo.from = dragInfo.start;
\r
3718 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3721 /* Event handler for mouse messages */
\r
3723 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3727 static int recursive = 0;
\r
3729 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3732 if (message == WM_MBUTTONUP) {
\r
3733 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3734 to the middle button: we simulate pressing the left button too!
\r
3736 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3737 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3743 pt.x = LOWORD(lParam);
\r
3744 pt.y = HIWORD(lParam);
\r
3745 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3746 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3747 if (!flipView && y >= 0) {
\r
3748 y = BOARD_HEIGHT - 1 - y;
\r
3750 if (flipView && x >= 0) {
\r
3751 x = BOARD_WIDTH - 1 - x;
\r
3754 switch (message) {
\r
3755 case WM_LBUTTONDOWN:
\r
3756 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3757 if (gameMode == EditPosition) {
\r
3758 SetWhiteToPlayEvent();
\r
3759 } else if (gameMode == IcsPlayingBlack ||
\r
3760 gameMode == MachinePlaysWhite) {
\r
3762 } else if (gameMode == EditGame) {
\r
3763 AdjustClock(flipClock, -1);
\r
3765 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3766 if (gameMode == EditPosition) {
\r
3767 SetBlackToPlayEvent();
\r
3768 } else if (gameMode == IcsPlayingWhite ||
\r
3769 gameMode == MachinePlaysBlack) {
\r
3771 } else if (gameMode == EditGame) {
\r
3772 AdjustClock(!flipClock, -1);
\r
3775 dragInfo.start.x = dragInfo.start.y = -1;
\r
3776 dragInfo.from = dragInfo.start;
\r
3777 if(fromX == -1 && frozen) { // not sure where this is for
\r
3778 fromX = fromY = -1;
\r
3779 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3782 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3783 DrawPosition(TRUE, NULL);
\r
3786 case WM_LBUTTONUP:
\r
3787 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3788 DrawPosition(TRUE, NULL);
\r
3791 case WM_MOUSEMOVE:
\r
3792 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3793 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3794 if ((appData.animateDragging || appData.highlightDragging)
\r
3795 && (wParam & MK_LBUTTON)
\r
3796 && dragInfo.from.x >= 0)
\r
3798 BOOL full_repaint = FALSE;
\r
3800 if (appData.animateDragging) {
\r
3801 dragInfo.pos = pt;
\r
3803 if (appData.highlightDragging) {
\r
3804 SetHighlights(fromX, fromY, x, y);
\r
3805 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3806 full_repaint = TRUE;
\r
3810 DrawPosition( full_repaint, NULL);
\r
3812 dragInfo.lastpos = dragInfo.pos;
\r
3816 case WM_MOUSEWHEEL: // [DM]
\r
3817 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3818 /* Mouse Wheel is being rolled forward
\r
3819 * Play moves forward
\r
3821 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3822 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3823 /* Mouse Wheel is being rolled backward
\r
3824 * Play moves backward
\r
3826 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3827 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3831 case WM_MBUTTONUP:
\r
3832 case WM_RBUTTONUP:
\r
3834 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3837 case WM_MBUTTONDOWN:
\r
3838 case WM_RBUTTONDOWN:
\r
3841 fromX = fromY = -1;
\r
3842 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3843 dragInfo.start.x = dragInfo.start.y = -1;
\r
3844 dragInfo.from = dragInfo.start;
\r
3845 dragInfo.lastpos = dragInfo.pos;
\r
3846 if (appData.highlightDragging) {
\r
3847 ClearHighlights();
\r
3850 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3851 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3852 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3853 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3854 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3857 DrawPosition(TRUE, NULL);
\r
3859 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3862 if (message == WM_MBUTTONDOWN) {
\r
3863 buttonCount = 3; /* even if system didn't think so */
\r
3864 if (wParam & MK_SHIFT)
\r
3865 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3867 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3868 } else { /* message == WM_RBUTTONDOWN */
\r
3869 /* Just have one menu, on the right button. Windows users don't
\r
3870 think to try the middle one, and sometimes other software steals
\r
3871 it, or it doesn't really exist. */
\r
3872 if(gameInfo.variant != VariantShogi)
\r
3873 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3875 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3879 SetCapture(hwndMain);
3882 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3883 SetupDropMenu(hmenu);
\r
3884 MenuPopup(hwnd, pt, hmenu, -1);
\r
3894 /* Preprocess messages for buttons in main window */
\r
3896 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3898 int id = GetWindowLong(hwnd, GWL_ID);
\r
3901 for (i=0; i<N_BUTTONS; i++) {
\r
3902 if (buttonDesc[i].id == id) break;
\r
3904 if (i == N_BUTTONS) return 0;
\r
3905 switch (message) {
\r
3910 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3911 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3918 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3921 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3922 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3923 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3924 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3926 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3928 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3929 PopUpMoveDialog((char)wParam);
\r
3935 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3938 /* Process messages for Promotion dialog box */
\r
3940 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3944 switch (message) {
\r
3945 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3946 /* Center the dialog over the application window */
\r
3947 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3948 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3949 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3950 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3951 SW_SHOW : SW_HIDE);
\r
3952 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3953 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3954 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3955 PieceToChar(WhiteAngel) != '~') ||
\r
3956 (PieceToChar(BlackAngel) >= 'A' &&
\r
3957 PieceToChar(BlackAngel) != '~') ) ?
\r
3958 SW_SHOW : SW_HIDE);
\r
3959 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3960 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3961 PieceToChar(WhiteMarshall) != '~') ||
\r
3962 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3963 PieceToChar(BlackMarshall) != '~') ) ?
\r
3964 SW_SHOW : SW_HIDE);
\r
3965 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3966 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3967 gameInfo.variant != VariantShogi ?
\r
3968 SW_SHOW : SW_HIDE);
\r
3969 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3970 gameInfo.variant != VariantShogi ?
\r
3971 SW_SHOW : SW_HIDE);
\r
3972 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3973 gameInfo.variant == VariantShogi ?
\r
3974 SW_SHOW : SW_HIDE);
\r
3975 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3976 gameInfo.variant == VariantShogi ?
\r
3977 SW_SHOW : SW_HIDE);
\r
3978 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3979 gameInfo.variant == VariantSuper ?
\r
3980 SW_SHOW : SW_HIDE);
\r
3983 case WM_COMMAND: /* message: received a command */
\r
3984 switch (LOWORD(wParam)) {
\r
3986 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3987 ClearHighlights();
\r
3988 DrawPosition(FALSE, NULL);
\r
3991 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3994 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3997 promoChar = PieceToChar(BlackRook);
\r
4000 promoChar = PieceToChar(BlackBishop);
\r
4002 case PB_Chancellor:
\r
4003 promoChar = PieceToChar(BlackMarshall);
\r
4005 case PB_Archbishop:
\r
4006 promoChar = PieceToChar(BlackAngel);
\r
4009 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4014 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4015 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4016 only show the popup when we are already sure the move is valid or
\r
4017 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4018 will figure out it is a promotion from the promoChar. */
\r
4019 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4020 fromX = fromY = -1;
\r
4021 if (!appData.highlightLastMove) {
\r
4022 ClearHighlights();
\r
4023 DrawPosition(FALSE, NULL);
\r
4030 /* Pop up promotion dialog */
\r
4032 PromotionPopup(HWND hwnd)
\r
4036 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4037 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4038 hwnd, (DLGPROC)lpProc);
\r
4039 FreeProcInstance(lpProc);
\r
4045 DrawPosition(TRUE, NULL);
\r
4046 PromotionPopup(hwndMain);
\r
4049 /* Toggle ShowThinking */
\r
4051 ToggleShowThinking()
\r
4053 appData.showThinking = !appData.showThinking;
\r
4054 ShowThinkingEvent();
\r
4058 LoadGameDialog(HWND hwnd, char* title)
\r
4062 char fileTitle[MSG_SIZ];
\r
4063 f = OpenFileDialog(hwnd, "rb", "",
\r
4064 appData.oldSaveStyle ? "gam" : "pgn",
\r
4066 title, &number, fileTitle, NULL);
\r
4068 cmailMsgLoaded = FALSE;
\r
4069 if (number == 0) {
\r
4070 int error = GameListBuild(f);
\r
4072 DisplayError("Cannot build game list", error);
\r
4073 } else if (!ListEmpty(&gameList) &&
\r
4074 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4075 GameListPopUp(f, fileTitle);
\r
4078 GameListDestroy();
\r
4081 LoadGame(f, number, fileTitle, FALSE);
\r
4085 int get_term_width()
\r
4090 HFONT hfont, hold_font;
\r
4095 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4099 // get the text metrics
\r
4100 hdc = GetDC(hText);
\r
4101 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4102 if (consoleCF.dwEffects & CFE_BOLD)
\r
4103 lf.lfWeight = FW_BOLD;
\r
4104 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4105 lf.lfItalic = TRUE;
\r
4106 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4107 lf.lfStrikeOut = TRUE;
\r
4108 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4109 lf.lfUnderline = TRUE;
\r
4110 hfont = CreateFontIndirect(&lf);
\r
4111 hold_font = SelectObject(hdc, hfont);
\r
4112 GetTextMetrics(hdc, &tm);
\r
4113 SelectObject(hdc, hold_font);
\r
4114 DeleteObject(hfont);
\r
4115 ReleaseDC(hText, hdc);
\r
4117 // get the rectangle
\r
4118 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4120 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4123 void UpdateICSWidth(HWND hText)
\r
4125 LONG old_width, new_width;
\r
4127 new_width = get_term_width(hText, FALSE);
\r
4128 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4129 if (new_width != old_width)
\r
4131 ics_update_width(new_width);
\r
4132 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4137 ChangedConsoleFont()
\r
4140 CHARRANGE tmpsel, sel;
\r
4141 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4142 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4143 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4146 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4147 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4148 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4149 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4150 * size. This was undocumented in the version of MSVC++ that I had
\r
4151 * when I wrote the code, but is apparently documented now.
\r
4153 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4154 cfmt.bCharSet = f->lf.lfCharSet;
\r
4155 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4156 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4157 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4158 /* Why are the following seemingly needed too? */
\r
4159 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4160 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4161 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4163 tmpsel.cpMax = -1; /*999999?*/
\r
4164 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4165 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4166 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4167 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4169 paraf.cbSize = sizeof(paraf);
\r
4170 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4171 paraf.dxStartIndent = 0;
\r
4172 paraf.dxOffset = WRAP_INDENT;
\r
4173 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4174 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4175 UpdateICSWidth(hText);
\r
4178 /*---------------------------------------------------------------------------*\
\r
4180 * Window Proc for main window
\r
4182 \*---------------------------------------------------------------------------*/
\r
4184 /* Process messages for main window, etc. */
\r
4186 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4189 int wmId, wmEvent;
\r
4193 char fileTitle[MSG_SIZ];
\r
4194 char buf[MSG_SIZ];
\r
4195 static SnapData sd;
\r
4197 switch (message) {
\r
4199 case WM_PAINT: /* message: repaint portion of window */
\r
4203 case WM_ERASEBKGND:
\r
4204 if (IsIconic(hwnd)) {
\r
4205 /* Cheat; change the message */
\r
4206 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4208 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4212 case WM_LBUTTONDOWN:
\r
4213 case WM_MBUTTONDOWN:
\r
4214 case WM_RBUTTONDOWN:
\r
4215 case WM_LBUTTONUP:
\r
4216 case WM_MBUTTONUP:
\r
4217 case WM_RBUTTONUP:
\r
4218 case WM_MOUSEMOVE:
\r
4219 case WM_MOUSEWHEEL:
\r
4220 MouseEvent(hwnd, message, wParam, lParam);
\r
4223 JAWS_KB_NAVIGATION
\r
4227 JAWS_ALT_INTERCEPT
\r
4229 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4230 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4231 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4232 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4234 SendMessage(h, message, wParam, lParam);
\r
4235 } else if(lParam != KF_REPEAT) {
\r
4236 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4237 PopUpMoveDialog((char)wParam);
\r
4238 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4239 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4244 case WM_PALETTECHANGED:
\r
4245 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4247 HDC hdc = GetDC(hwndMain);
\r
4248 SelectPalette(hdc, hPal, TRUE);
\r
4249 nnew = RealizePalette(hdc);
\r
4251 paletteChanged = TRUE;
\r
4252 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4254 ReleaseDC(hwnd, hdc);
\r
4258 case WM_QUERYNEWPALETTE:
\r
4259 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4261 HDC hdc = GetDC(hwndMain);
\r
4262 paletteChanged = FALSE;
\r
4263 SelectPalette(hdc, hPal, FALSE);
\r
4264 nnew = RealizePalette(hdc);
\r
4266 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4268 ReleaseDC(hwnd, hdc);
\r
4273 case WM_COMMAND: /* message: command from application menu */
\r
4274 wmId = LOWORD(wParam);
\r
4275 wmEvent = HIWORD(wParam);
\r
4280 SAY("new game enter a move to play against the computer with white");
\r
4283 case IDM_NewGameFRC:
\r
4284 if( NewGameFRC() == 0 ) {
\r
4289 case IDM_NewVariant:
\r
4290 NewVariantPopup(hwnd);
\r
4293 case IDM_LoadGame:
\r
4294 LoadGameDialog(hwnd, "Load Game from File");
\r
4297 case IDM_LoadNextGame:
\r
4301 case IDM_LoadPrevGame:
\r
4305 case IDM_ReloadGame:
\r
4309 case IDM_LoadPosition:
\r
4310 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4311 Reset(FALSE, TRUE);
\r
4314 f = OpenFileDialog(hwnd, "rb", "",
\r
4315 appData.oldSaveStyle ? "pos" : "fen",
\r
4317 "Load Position from File", &number, fileTitle, NULL);
\r
4319 LoadPosition(f, number, fileTitle);
\r
4323 case IDM_LoadNextPosition:
\r
4324 ReloadPosition(1);
\r
4327 case IDM_LoadPrevPosition:
\r
4328 ReloadPosition(-1);
\r
4331 case IDM_ReloadPosition:
\r
4332 ReloadPosition(0);
\r
4335 case IDM_SaveGame:
\r
4336 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4337 f = OpenFileDialog(hwnd, "a", defName,
\r
4338 appData.oldSaveStyle ? "gam" : "pgn",
\r
4340 "Save Game to File", NULL, fileTitle, NULL);
\r
4342 SaveGame(f, 0, "");
\r
4346 case IDM_SavePosition:
\r
4347 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4348 f = OpenFileDialog(hwnd, "a", defName,
\r
4349 appData.oldSaveStyle ? "pos" : "fen",
\r
4351 "Save Position to File", NULL, fileTitle, NULL);
\r
4353 SavePosition(f, 0, "");
\r
4357 case IDM_SaveDiagram:
\r
4358 defName = "diagram";
\r
4359 f = OpenFileDialog(hwnd, "wb", defName,
\r
4362 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4368 case IDM_CopyGame:
\r
4369 CopyGameToClipboard();
\r
4372 case IDM_PasteGame:
\r
4373 PasteGameFromClipboard();
\r
4376 case IDM_CopyGameListToClipboard:
\r
4377 CopyGameListToClipboard();
\r
4380 /* [AS] Autodetect FEN or PGN data */
\r
4381 case IDM_PasteAny:
\r
4382 PasteGameOrFENFromClipboard();
\r
4385 /* [AS] Move history */
\r
4386 case IDM_ShowMoveHistory:
\r
4387 if( MoveHistoryIsUp() ) {
\r
4388 MoveHistoryPopDown();
\r
4391 MoveHistoryPopUp();
\r
4395 /* [AS] Eval graph */
\r
4396 case IDM_ShowEvalGraph:
\r
4397 if( EvalGraphIsUp() ) {
\r
4398 EvalGraphPopDown();
\r
4402 SetFocus(hwndMain);
\r
4406 /* [AS] Engine output */
\r
4407 case IDM_ShowEngineOutput:
\r
4408 if( EngineOutputIsUp() ) {
\r
4409 EngineOutputPopDown();
\r
4412 EngineOutputPopUp();
\r
4416 /* [AS] User adjudication */
\r
4417 case IDM_UserAdjudication_White:
\r
4418 UserAdjudicationEvent( +1 );
\r
4421 case IDM_UserAdjudication_Black:
\r
4422 UserAdjudicationEvent( -1 );
\r
4425 case IDM_UserAdjudication_Draw:
\r
4426 UserAdjudicationEvent( 0 );
\r
4429 /* [AS] Game list options dialog */
\r
4430 case IDM_GameListOptions:
\r
4431 GameListOptions();
\r
4438 case IDM_CopyPosition:
\r
4439 CopyFENToClipboard();
\r
4442 case IDM_PastePosition:
\r
4443 PasteFENFromClipboard();
\r
4446 case IDM_MailMove:
\r
4450 case IDM_ReloadCMailMsg:
\r
4451 Reset(TRUE, TRUE);
\r
4452 ReloadCmailMsgEvent(FALSE);
\r
4455 case IDM_Minimize:
\r
4456 ShowWindow(hwnd, SW_MINIMIZE);
\r
4463 case IDM_MachineWhite:
\r
4464 MachineWhiteEvent();
\r
4466 * refresh the tags dialog only if it's visible
\r
4468 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4470 tags = PGNTags(&gameInfo);
\r
4471 TagsPopUp(tags, CmailMsg());
\r
4474 SAY("computer starts playing white");
\r
4477 case IDM_MachineBlack:
\r
4478 MachineBlackEvent();
\r
4480 * refresh the tags dialog only if it's visible
\r
4482 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4484 tags = PGNTags(&gameInfo);
\r
4485 TagsPopUp(tags, CmailMsg());
\r
4488 SAY("computer starts playing black");
\r
4491 case IDM_TwoMachines:
\r
4492 TwoMachinesEvent();
\r
4494 * refresh the tags dialog only if it's visible
\r
4496 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4498 tags = PGNTags(&gameInfo);
\r
4499 TagsPopUp(tags, CmailMsg());
\r
4502 SAY("programs start playing each other");
\r
4505 case IDM_AnalysisMode:
\r
4506 if (!first.analysisSupport) {
\r
4507 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4508 DisplayError(buf, 0);
\r
4510 SAY("analyzing current position");
\r
4511 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4512 if (appData.icsActive) {
\r
4513 if (gameMode != IcsObserving) {
\r
4514 sprintf(buf, "You are not observing a game");
\r
4515 DisplayError(buf, 0);
\r
4516 /* secure check */
\r
4517 if (appData.icsEngineAnalyze) {
\r
4518 if (appData.debugMode)
\r
4519 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4520 ExitAnalyzeMode();
\r
4526 /* if enable, user want disable icsEngineAnalyze */
\r
4527 if (appData.icsEngineAnalyze) {
\r
4528 ExitAnalyzeMode();
\r
4532 appData.icsEngineAnalyze = TRUE;
\r
4533 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4536 if (!appData.showThinking) ToggleShowThinking();
\r
4537 AnalyzeModeEvent();
\r
4541 case IDM_AnalyzeFile:
\r
4542 if (!first.analysisSupport) {
\r
4543 char buf[MSG_SIZ];
\r
4544 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4545 DisplayError(buf, 0);
\r
4547 if (!appData.showThinking) ToggleShowThinking();
\r
4548 AnalyzeFileEvent();
\r
4549 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4550 AnalysisPeriodicEvent(1);
\r
4554 case IDM_IcsClient:
\r
4558 case IDM_EditGame:
\r
4563 case IDM_EditPosition:
\r
4564 EditPositionEvent();
\r
4565 SAY("to set up a position type a FEN");
\r
4568 case IDM_Training:
\r
4572 case IDM_ShowGameList:
\r
4573 ShowGameListProc();
\r
4576 case IDM_EditTags:
\r
4580 case IDM_EditComment:
\r
4581 if (commentUp && editComment) {
\r
4584 EditCommentEvent();
\r
4604 case IDM_CallFlag:
\r
4624 case IDM_StopObserving:
\r
4625 StopObservingEvent();
\r
4628 case IDM_StopExamining:
\r
4629 StopExaminingEvent();
\r
4633 UploadGameEvent();
\r
4636 case IDM_TypeInMove:
\r
4637 PopUpMoveDialog('\000');
\r
4640 case IDM_TypeInName:
\r
4641 PopUpNameDialog('\000');
\r
4644 case IDM_Backward:
\r
4646 SetFocus(hwndMain);
\r
4653 SetFocus(hwndMain);
\r
4658 SetFocus(hwndMain);
\r
4663 SetFocus(hwndMain);
\r
4667 RevertEvent(FALSE);
\r
4670 case IDM_Annotate: // [HGM] vari: revert with annotation
\r
4671 RevertEvent(TRUE);
\r
4674 case IDM_TruncateGame:
\r
4675 TruncateGameEvent();
\r
4682 case IDM_RetractMove:
\r
4683 RetractMoveEvent();
\r
4686 case IDM_FlipView:
\r
4687 flipView = !flipView;
\r
4688 DrawPosition(FALSE, NULL);
\r
4691 case IDM_FlipClock:
\r
4692 flipClock = !flipClock;
\r
4693 DisplayBothClocks();
\r
4694 DrawPosition(FALSE, NULL);
\r
4697 case IDM_MuteSounds:
\r
4698 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4699 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4700 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4703 case IDM_GeneralOptions:
\r
4704 GeneralOptionsPopup(hwnd);
\r
4705 DrawPosition(TRUE, NULL);
\r
4708 case IDM_BoardOptions:
\r
4709 BoardOptionsPopup(hwnd);
\r
4712 case IDM_EnginePlayOptions:
\r
4713 EnginePlayOptionsPopup(hwnd);
\r
4716 case IDM_Engine1Options:
\r
4717 EngineOptionsPopup(hwnd, &first);
\r
4720 case IDM_Engine2Options:
\r
4721 EngineOptionsPopup(hwnd, &second);
\r
4724 case IDM_OptionsUCI:
\r
4725 UciOptionsPopup(hwnd);
\r
4728 case IDM_IcsOptions:
\r
4729 IcsOptionsPopup(hwnd);
\r
4733 FontsOptionsPopup(hwnd);
\r
4737 SoundOptionsPopup(hwnd);
\r
4740 case IDM_CommPort:
\r
4741 CommPortOptionsPopup(hwnd);
\r
4744 case IDM_LoadOptions:
\r
4745 LoadOptionsPopup(hwnd);
\r
4748 case IDM_SaveOptions:
\r
4749 SaveOptionsPopup(hwnd);
\r
4752 case IDM_TimeControl:
\r
4753 TimeControlOptionsPopup(hwnd);
\r
4756 case IDM_SaveSettings:
\r
4757 SaveSettings(settingsFileName);
\r
4760 case IDM_SaveSettingsOnExit:
\r
4761 saveSettingsOnExit = !saveSettingsOnExit;
\r
4762 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4763 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4764 MF_CHECKED : MF_UNCHECKED));
\r
4775 case IDM_AboutGame:
\r
4780 appData.debugMode = !appData.debugMode;
\r
4781 if (appData.debugMode) {
\r
4782 char dir[MSG_SIZ];
\r
4783 GetCurrentDirectory(MSG_SIZ, dir);
\r
4784 SetCurrentDirectory(installDir);
\r
4785 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4786 SetCurrentDirectory(dir);
\r
4787 setbuf(debugFP, NULL);
\r
4794 case IDM_HELPCONTENTS:
\r
4795 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4796 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4797 MessageBox (GetFocus(),
\r
4798 "Unable to activate help",
\r
4799 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4803 case IDM_HELPSEARCH:
\r
4804 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4805 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4806 MessageBox (GetFocus(),
\r
4807 "Unable to activate help",
\r
4808 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4812 case IDM_HELPHELP:
\r
4813 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4814 MessageBox (GetFocus(),
\r
4815 "Unable to activate help",
\r
4816 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4821 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4823 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4824 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4825 FreeProcInstance(lpProc);
\r
4828 case IDM_DirectCommand1:
\r
4829 AskQuestionEvent("Direct Command",
\r
4830 "Send to chess program:", "", "1");
\r
4832 case IDM_DirectCommand2:
\r
4833 AskQuestionEvent("Direct Command",
\r
4834 "Send to second chess program:", "", "2");
\r
4837 case EP_WhitePawn:
\r
4838 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4839 fromX = fromY = -1;
\r
4842 case EP_WhiteKnight:
\r
4843 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4844 fromX = fromY = -1;
\r
4847 case EP_WhiteBishop:
\r
4848 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4849 fromX = fromY = -1;
\r
4852 case EP_WhiteRook:
\r
4853 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4854 fromX = fromY = -1;
\r
4857 case EP_WhiteQueen:
\r
4858 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4859 fromX = fromY = -1;
\r
4862 case EP_WhiteFerz:
\r
4863 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4864 fromX = fromY = -1;
\r
4867 case EP_WhiteWazir:
\r
4868 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4869 fromX = fromY = -1;
\r
4872 case EP_WhiteAlfil:
\r
4873 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4874 fromX = fromY = -1;
\r
4877 case EP_WhiteCannon:
\r
4878 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4879 fromX = fromY = -1;
\r
4882 case EP_WhiteCardinal:
\r
4883 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4884 fromX = fromY = -1;
\r
4887 case EP_WhiteMarshall:
\r
4888 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4889 fromX = fromY = -1;
\r
4892 case EP_WhiteKing:
\r
4893 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4894 fromX = fromY = -1;
\r
4897 case EP_BlackPawn:
\r
4898 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4899 fromX = fromY = -1;
\r
4902 case EP_BlackKnight:
\r
4903 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4904 fromX = fromY = -1;
\r
4907 case EP_BlackBishop:
\r
4908 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4909 fromX = fromY = -1;
\r
4912 case EP_BlackRook:
\r
4913 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4914 fromX = fromY = -1;
\r
4917 case EP_BlackQueen:
\r
4918 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4919 fromX = fromY = -1;
\r
4922 case EP_BlackFerz:
\r
4923 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4924 fromX = fromY = -1;
\r
4927 case EP_BlackWazir:
\r
4928 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4929 fromX = fromY = -1;
\r
4932 case EP_BlackAlfil:
\r
4933 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4934 fromX = fromY = -1;
\r
4937 case EP_BlackCannon:
\r
4938 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4939 fromX = fromY = -1;
\r
4942 case EP_BlackCardinal:
\r
4943 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4944 fromX = fromY = -1;
\r
4947 case EP_BlackMarshall:
\r
4948 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4949 fromX = fromY = -1;
\r
4952 case EP_BlackKing:
\r
4953 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4954 fromX = fromY = -1;
\r
4957 case EP_EmptySquare:
\r
4958 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4959 fromX = fromY = -1;
\r
4962 case EP_ClearBoard:
\r
4963 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4964 fromX = fromY = -1;
\r
4968 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4969 fromX = fromY = -1;
\r
4973 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4974 fromX = fromY = -1;
\r
4978 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4979 fromX = fromY = -1;
\r
4983 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4984 fromX = fromY = -1;
\r
4988 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4989 fromX = fromY = -1;
\r
4993 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4994 fromX = fromY = -1;
\r
4998 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4999 fromX = fromY = -1;
\r
5003 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5004 fromX = fromY = -1;
\r
5008 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5009 fromX = fromY = -1;
\r
5013 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5019 case CLOCK_TIMER_ID:
\r
5020 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5021 clockTimerEvent = 0;
\r
5022 DecrementClocks(); /* call into back end */
\r
5024 case LOAD_GAME_TIMER_ID:
\r
5025 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5026 loadGameTimerEvent = 0;
\r
5027 AutoPlayGameLoop(); /* call into back end */
\r
5029 case ANALYSIS_TIMER_ID:
\r
5030 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5031 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5032 AnalysisPeriodicEvent(0);
\r
5034 KillTimer(hwnd, analysisTimerEvent);
\r
5035 analysisTimerEvent = 0;
\r
5038 case DELAYED_TIMER_ID:
\r
5039 KillTimer(hwnd, delayedTimerEvent);
\r
5040 delayedTimerEvent = 0;
\r
5041 delayedTimerCallback();
\r
5046 case WM_USER_Input:
\r
5047 InputEvent(hwnd, message, wParam, lParam);
\r
5050 /* [AS] Also move "attached" child windows */
\r
5051 case WM_WINDOWPOSCHANGING:
\r
5053 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5054 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5056 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5057 /* Window is moving */
\r
5060 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5061 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5062 rcMain.right = wpMain.x + wpMain.width;
\r
5063 rcMain.top = wpMain.y;
\r
5064 rcMain.bottom = wpMain.y + wpMain.height;
\r
5066 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5067 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5068 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5069 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5070 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5071 wpMain.x = lpwp->x;
\r
5072 wpMain.y = lpwp->y;
\r
5077 /* [AS] Snapping */
\r
5078 case WM_ENTERSIZEMOVE:
\r
5079 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5080 if (hwnd == hwndMain) {
\r
5081 doingSizing = TRUE;
\r
5084 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5088 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5089 if (hwnd == hwndMain) {
\r
5090 lastSizing = wParam;
\r
5095 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5096 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5098 case WM_EXITSIZEMOVE:
\r
5099 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5100 if (hwnd == hwndMain) {
\r
5102 doingSizing = FALSE;
\r
5103 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5104 GetClientRect(hwnd, &client);
\r
5105 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5107 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5109 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5112 case WM_DESTROY: /* message: window being destroyed */
\r
5113 PostQuitMessage(0);
\r
5117 if (hwnd == hwndMain) {
\r
5122 default: /* Passes it on if unprocessed */
\r
5123 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5128 /*---------------------------------------------------------------------------*\
\r
5130 * Misc utility routines
\r
5132 \*---------------------------------------------------------------------------*/
\r
5135 * Decent random number generator, at least not as bad as Windows
\r
5136 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5138 unsigned int randstate;
\r
5143 randstate = randstate * 1664525 + 1013904223;
\r
5144 return (int) randstate & 0x7fffffff;
\r
5148 mysrandom(unsigned int seed)
\r
5155 * returns TRUE if user selects a different color, FALSE otherwise
\r
5159 ChangeColor(HWND hwnd, COLORREF *which)
\r
5161 static BOOL firstTime = TRUE;
\r
5162 static DWORD customColors[16];
\r
5164 COLORREF newcolor;
\r
5169 /* Make initial colors in use available as custom colors */
\r
5170 /* Should we put the compiled-in defaults here instead? */
\r
5172 customColors[i++] = lightSquareColor & 0xffffff;
\r
5173 customColors[i++] = darkSquareColor & 0xffffff;
\r
5174 customColors[i++] = whitePieceColor & 0xffffff;
\r
5175 customColors[i++] = blackPieceColor & 0xffffff;
\r
5176 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5177 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5179 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5180 customColors[i++] = textAttribs[ccl].color;
\r
5182 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5183 firstTime = FALSE;
\r
5186 cc.lStructSize = sizeof(cc);
\r
5187 cc.hwndOwner = hwnd;
\r
5188 cc.hInstance = NULL;
\r
5189 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5190 cc.lpCustColors = (LPDWORD) customColors;
\r
5191 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5193 if (!ChooseColor(&cc)) return FALSE;
\r
5195 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5196 if (newcolor == *which) return FALSE;
\r
5197 *which = newcolor;
\r
5201 InitDrawingColors();
\r
5202 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5207 MyLoadSound(MySound *ms)
\r
5213 if (ms->data) free(ms->data);
\r
5216 switch (ms->name[0]) {
\r
5222 /* System sound from Control Panel. Don't preload here. */
\r
5226 if (ms->name[1] == NULLCHAR) {
\r
5227 /* "!" alone = silence */
\r
5230 /* Builtin wave resource. Error if not found. */
\r
5231 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5232 if (h == NULL) break;
\r
5233 ms->data = (void *)LoadResource(hInst, h);
\r
5234 if (h == NULL) break;
\r
5239 /* .wav file. Error if not found. */
\r
5240 f = fopen(ms->name, "rb");
\r
5241 if (f == NULL) break;
\r
5242 if (fstat(fileno(f), &st) < 0) break;
\r
5243 ms->data = malloc(st.st_size);
\r
5244 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5250 char buf[MSG_SIZ];
\r
5251 sprintf(buf, "Error loading sound %s", ms->name);
\r
5252 DisplayError(buf, GetLastError());
\r
5258 MyPlaySound(MySound *ms)
\r
5260 BOOLEAN ok = FALSE;
\r
5262 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5263 switch (ms->name[0]) {
\r
5265 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5270 /* System sound from Control Panel (deprecated feature).
\r
5271 "$" alone or an unset sound name gets default beep (still in use). */
\r
5272 if (ms->name[1]) {
\r
5273 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5275 if (!ok) ok = MessageBeep(MB_OK);
\r
5278 /* Builtin wave resource, or "!" alone for silence */
\r
5279 if (ms->name[1]) {
\r
5280 if (ms->data == NULL) return FALSE;
\r
5281 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5287 /* .wav file. Error if not found. */
\r
5288 if (ms->data == NULL) return FALSE;
\r
5289 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5292 /* Don't print an error: this can happen innocently if the sound driver
\r
5293 is busy; for instance, if another instance of WinBoard is playing
\r
5294 a sound at about the same time. */
\r
5300 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5303 OPENFILENAME *ofn;
\r
5304 static UINT *number; /* gross that this is static */
\r
5306 switch (message) {
\r
5307 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5308 /* Center the dialog over the application window */
\r
5309 ofn = (OPENFILENAME *) lParam;
\r
5310 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5311 number = (UINT *) ofn->lCustData;
\r
5312 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5316 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5317 return FALSE; /* Allow for further processing */
\r
5320 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5321 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5323 return FALSE; /* Allow for further processing */
\r
5329 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5331 static UINT *number;
\r
5332 OPENFILENAME *ofname;
\r
5335 case WM_INITDIALOG:
\r
5336 ofname = (OPENFILENAME *)lParam;
\r
5337 number = (UINT *)(ofname->lCustData);
\r
5340 ofnot = (OFNOTIFY *)lParam;
\r
5341 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5342 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5351 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5352 char *nameFilt, char *dlgTitle, UINT *number,
\r
5353 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5355 OPENFILENAME openFileName;
\r
5356 char buf1[MSG_SIZ];
\r
5359 if (fileName == NULL) fileName = buf1;
\r
5360 if (defName == NULL) {
\r
5361 strcpy(fileName, "*.");
\r
5362 strcat(fileName, defExt);
\r
5364 strcpy(fileName, defName);
\r
5366 if (fileTitle) strcpy(fileTitle, "");
\r
5367 if (number) *number = 0;
\r
5369 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5370 openFileName.hwndOwner = hwnd;
\r
5371 openFileName.hInstance = (HANDLE) hInst;
\r
5372 openFileName.lpstrFilter = nameFilt;
\r
5373 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5374 openFileName.nMaxCustFilter = 0L;
\r
5375 openFileName.nFilterIndex = 1L;
\r
5376 openFileName.lpstrFile = fileName;
\r
5377 openFileName.nMaxFile = MSG_SIZ;
\r
5378 openFileName.lpstrFileTitle = fileTitle;
\r
5379 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5380 openFileName.lpstrInitialDir = NULL;
\r
5381 openFileName.lpstrTitle = dlgTitle;
\r
5382 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5383 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5384 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5385 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5386 openFileName.nFileOffset = 0;
\r
5387 openFileName.nFileExtension = 0;
\r
5388 openFileName.lpstrDefExt = defExt;
\r
5389 openFileName.lCustData = (LONG) number;
\r
5390 openFileName.lpfnHook = oldDialog ?
\r
5391 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5392 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5394 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5395 GetOpenFileName(&openFileName)) {
\r
5396 /* open the file */
\r
5397 f = fopen(openFileName.lpstrFile, write);
\r
5399 MessageBox(hwnd, "File open failed", NULL,
\r
5400 MB_OK|MB_ICONEXCLAMATION);
\r
5404 int err = CommDlgExtendedError();
\r
5405 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5414 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5416 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5419 * Get the first pop-up menu in the menu template. This is the
\r
5420 * menu that TrackPopupMenu displays.
\r
5422 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5424 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5427 * TrackPopup uses screen coordinates, so convert the
\r
5428 * coordinates of the mouse click to screen coordinates.
\r
5430 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5432 /* Draw and track the floating pop-up menu. */
\r
5433 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5434 pt.x, pt.y, 0, hwnd, NULL);
\r
5436 /* Destroy the menu.*/
\r
5437 DestroyMenu(hmenu);
\r
5442 int sizeX, sizeY, newSizeX, newSizeY;
\r
5444 } ResizeEditPlusButtonsClosure;
\r
5447 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5449 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5453 if (hChild == cl->hText) return TRUE;
\r
5454 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5455 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5456 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5457 ScreenToClient(cl->hDlg, &pt);
\r
5458 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5459 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5463 /* Resize a dialog that has a (rich) edit field filling most of
\r
5464 the top, with a row of buttons below */
\r
5466 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5469 int newTextHeight, newTextWidth;
\r
5470 ResizeEditPlusButtonsClosure cl;
\r
5472 /*if (IsIconic(hDlg)) return;*/
\r
5473 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5475 cl.hdwp = BeginDeferWindowPos(8);
\r
5477 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5478 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5479 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5480 if (newTextHeight < 0) {
\r
5481 newSizeY += -newTextHeight;
\r
5482 newTextHeight = 0;
\r
5484 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5485 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5491 cl.newSizeX = newSizeX;
\r
5492 cl.newSizeY = newSizeY;
\r
5493 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5495 EndDeferWindowPos(cl.hdwp);
\r
5498 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5500 RECT rChild, rParent;
\r
5501 int wChild, hChild, wParent, hParent;
\r
5502 int wScreen, hScreen, xNew, yNew;
\r
5505 /* Get the Height and Width of the child window */
\r
5506 GetWindowRect (hwndChild, &rChild);
\r
5507 wChild = rChild.right - rChild.left;
\r
5508 hChild = rChild.bottom - rChild.top;
\r
5510 /* Get the Height and Width of the parent window */
\r
5511 GetWindowRect (hwndParent, &rParent);
\r
5512 wParent = rParent.right - rParent.left;
\r
5513 hParent = rParent.bottom - rParent.top;
\r
5515 /* Get the display limits */
\r
5516 hdc = GetDC (hwndChild);
\r
5517 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5518 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5519 ReleaseDC(hwndChild, hdc);
\r
5521 /* Calculate new X position, then adjust for screen */
\r
5522 xNew = rParent.left + ((wParent - wChild) /2);
\r
5525 } else if ((xNew+wChild) > wScreen) {
\r
5526 xNew = wScreen - wChild;
\r
5529 /* Calculate new Y position, then adjust for screen */
\r
5531 yNew = rParent.top + ((hParent - hChild) /2);
\r
5534 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5539 } else if ((yNew+hChild) > hScreen) {
\r
5540 yNew = hScreen - hChild;
\r
5543 /* Set it, and return */
\r
5544 return SetWindowPos (hwndChild, NULL,
\r
5545 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5548 /* Center one window over another */
\r
5549 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5551 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5554 /*---------------------------------------------------------------------------*\
\r
5556 * Startup Dialog functions
\r
5558 \*---------------------------------------------------------------------------*/
\r
5560 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5562 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5564 while (*cd != NULL) {
\r
5565 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5571 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5573 char buf1[MAX_ARG_LEN];
\r
5576 if (str[0] == '@') {
\r
5577 FILE* f = fopen(str + 1, "r");
\r
5579 DisplayFatalError(str + 1, errno, 2);
\r
5582 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5584 buf1[len] = NULLCHAR;
\r
5588 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5591 char buf[MSG_SIZ];
\r
5592 char *end = strchr(str, '\n');
\r
5593 if (end == NULL) return;
\r
5594 memcpy(buf, str, end - str);
\r
5595 buf[end - str] = NULLCHAR;
\r
5596 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5602 SetStartupDialogEnables(HWND hDlg)
\r
5604 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5605 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5606 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5607 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5608 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5609 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5610 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5611 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5612 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5613 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5614 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5615 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5616 IsDlgButtonChecked(hDlg, OPT_View));
\r
5620 QuoteForFilename(char *filename)
\r
5622 int dquote, space;
\r
5623 dquote = strchr(filename, '"') != NULL;
\r
5624 space = strchr(filename, ' ') != NULL;
\r
5625 if (dquote || space) {
\r
5637 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5639 char buf[MSG_SIZ];
\r
5642 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5643 q = QuoteForFilename(nthcp);
\r
5644 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5645 if (*nthdir != NULLCHAR) {
\r
5646 q = QuoteForFilename(nthdir);
\r
5647 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5649 if (*nthcp == NULLCHAR) {
\r
5650 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5651 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5652 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5653 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5658 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5660 char buf[MSG_SIZ];
\r
5664 switch (message) {
\r
5665 case WM_INITDIALOG:
\r
5666 /* Center the dialog */
\r
5667 CenterWindow (hDlg, GetDesktopWindow());
\r
5668 /* Initialize the dialog items */
\r
5669 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5670 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5671 firstChessProgramNames);
\r
5672 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5673 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5674 secondChessProgramNames);
\r
5675 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5676 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5677 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5678 if (*appData.icsHelper != NULLCHAR) {
\r
5679 char *q = QuoteForFilename(appData.icsHelper);
\r
5680 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5682 if (*appData.icsHost == NULLCHAR) {
\r
5683 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5684 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5685 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5686 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5687 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5690 if (appData.icsActive) {
\r
5691 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5693 else if (appData.noChessProgram) {
\r
5694 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5697 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5700 SetStartupDialogEnables(hDlg);
\r
5704 switch (LOWORD(wParam)) {
\r
5706 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5707 strcpy(buf, "/fcp=");
\r
5708 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5710 ParseArgs(StringGet, &p);
\r
5711 strcpy(buf, "/scp=");
\r
5712 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5714 ParseArgs(StringGet, &p);
\r
5715 appData.noChessProgram = FALSE;
\r
5716 appData.icsActive = FALSE;
\r
5717 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5718 strcpy(buf, "/ics /icshost=");
\r
5719 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5721 ParseArgs(StringGet, &p);
\r
5722 if (appData.zippyPlay) {
\r
5723 strcpy(buf, "/fcp=");
\r
5724 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5726 ParseArgs(StringGet, &p);
\r
5728 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5729 appData.noChessProgram = TRUE;
\r
5730 appData.icsActive = FALSE;
\r
5732 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5733 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5736 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5737 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5739 ParseArgs(StringGet, &p);
\r
5741 EndDialog(hDlg, TRUE);
\r
5748 case IDM_HELPCONTENTS:
\r
5749 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5750 MessageBox (GetFocus(),
\r
5751 "Unable to activate help",
\r
5752 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5757 SetStartupDialogEnables(hDlg);
\r
5765 /*---------------------------------------------------------------------------*\
\r
5767 * About box dialog functions
\r
5769 \*---------------------------------------------------------------------------*/
\r
5771 /* Process messages for "About" dialog box */
\r
5773 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5775 switch (message) {
\r
5776 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5777 /* Center the dialog over the application window */
\r
5778 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5779 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5783 case WM_COMMAND: /* message: received a command */
\r
5784 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5785 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5786 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5794 /*---------------------------------------------------------------------------*\
\r
5796 * Comment Dialog functions
\r
5798 \*---------------------------------------------------------------------------*/
\r
5801 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5803 static HANDLE hwndText = NULL;
\r
5804 int len, newSizeX, newSizeY, flags;
\r
5805 static int sizeX, sizeY;
\r
5810 switch (message) {
\r
5811 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5812 /* Initialize the dialog items */
\r
5813 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5814 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5815 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5816 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5817 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5818 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5819 SetWindowText(hDlg, commentTitle);
\r
5820 if (editComment) {
\r
5821 SetFocus(hwndText);
\r
5823 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5825 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5826 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5827 MAKELPARAM(FALSE, 0));
\r
5828 /* Size and position the dialog */
\r
5829 if (!commentDialog) {
\r
5830 commentDialog = hDlg;
\r
5831 flags = SWP_NOZORDER;
\r
5832 GetClientRect(hDlg, &rect);
\r
5833 sizeX = rect.right;
\r
5834 sizeY = rect.bottom;
\r
5835 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5836 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5837 WINDOWPLACEMENT wp;
\r
5838 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5839 wp.length = sizeof(WINDOWPLACEMENT);
\r
5841 wp.showCmd = SW_SHOW;
\r
5842 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5843 wp.rcNormalPosition.left = wpComment.x;
\r
5844 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5845 wp.rcNormalPosition.top = wpComment.y;
\r
5846 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5847 SetWindowPlacement(hDlg, &wp);
\r
5849 GetClientRect(hDlg, &rect);
\r
5850 newSizeX = rect.right;
\r
5851 newSizeY = rect.bottom;
\r
5852 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5853 newSizeX, newSizeY);
\r
5858 SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );
\r
5861 case WM_COMMAND: /* message: received a command */
\r
5862 switch (LOWORD(wParam)) {
\r
5864 if (editComment) {
\r
5866 /* Read changed options from the dialog box */
\r
5867 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5868 len = GetWindowTextLength(hwndText);
\r
5869 str = (char *) malloc(len + 1);
\r
5870 GetWindowText(hwndText, str, len + 1);
\r
5879 ReplaceComment(commentIndex, str);
\r
5886 case OPT_CancelComment:
\r
5890 case OPT_ClearComment:
\r
5891 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5894 case OPT_EditComment:
\r
5895 EditCommentEvent();
\r
5903 case WM_NOTIFY: // [HGM] vari: cloned from whistory.c
\r
5904 if( wParam == OPT_CommentText ) {
\r
5905 MSGFILTER * lpMF = (MSGFILTER *) lParam;
\r
5907 if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {
\r
5911 pt.x = LOWORD( lpMF->lParam );
\r
5912 pt.y = HIWORD( lpMF->lParam );
\r
5914 index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );
\r
5916 hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above
\r
5917 len = GetWindowTextLength(hwndText);
\r
5918 str = (char *) malloc(len + 1);
\r
5919 GetWindowText(hwndText, str, len + 1);
\r
5920 ReplaceComment(commentIndex, str);
\r
5921 if(commentIndex != currentMove) ToNrEvent(commentIndex);
\r
5922 LoadVariation( index, str ); // [HGM] also does the actual moving to it, now
\r
5925 /* Zap the message for good: apparently, returning non-zero is not enough */
\r
5926 lpMF->msg = WM_USER;
\r
5934 newSizeX = LOWORD(lParam);
\r
5935 newSizeY = HIWORD(lParam);
\r
5936 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5941 case WM_GETMINMAXINFO:
\r
5942 /* Prevent resizing window too small */
\r
5943 mmi = (MINMAXINFO *) lParam;
\r
5944 mmi->ptMinTrackSize.x = 100;
\r
5945 mmi->ptMinTrackSize.y = 100;
\r
5952 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5957 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5959 if (str == NULL) str = "";
\r
5960 p = (char *) malloc(2 * strlen(str) + 2);
\r
5963 if (*str == '\n') *q++ = '\r';
\r
5967 if (commentText != NULL) free(commentText);
\r
5969 commentIndex = index;
\r
5970 commentTitle = title;
\r
5972 editComment = edit;
\r
5974 if (commentDialog) {
\r
5975 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5976 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5978 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5979 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5980 hwndMain, (DLGPROC)lpProc);
\r
5981 FreeProcInstance(lpProc);
\r
5987 /*---------------------------------------------------------------------------*\
\r
5989 * Type-in move dialog functions
\r
5991 \*---------------------------------------------------------------------------*/
\r
5994 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5996 char move[MSG_SIZ];
\r
5998 ChessMove moveType;
\r
5999 int fromX, fromY, toX, toY;
\r
6002 switch (message) {
\r
6003 case WM_INITDIALOG:
\r
6004 move[0] = (char) lParam;
\r
6005 move[1] = NULLCHAR;
\r
6006 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6007 hInput = GetDlgItem(hDlg, OPT_Move);
\r
6008 SetWindowText(hInput, move);
\r
6010 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6014 switch (LOWORD(wParam)) {
\r
6016 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
6017 { int n; Board board;
\r
6019 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
6020 EditPositionPasteFEN(move);
\r
6021 EndDialog(hDlg, TRUE);
\r
6024 // [HGM] movenum: allow move number to be typed in any mode
\r
6025 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
6027 EndDialog(hDlg, TRUE);
\r
6031 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
6032 gameMode != Training) {
\r
6033 DisplayMoveError("Displayed move is not current");
\r
6035 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6036 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6037 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6038 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6039 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6040 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6041 if (gameMode != Training)
\r
6042 forwardMostMove = currentMove;
\r
6043 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6045 DisplayMoveError("Could not parse move");
\r
6048 EndDialog(hDlg, TRUE);
\r
6051 EndDialog(hDlg, FALSE);
\r
6062 PopUpMoveDialog(char firstchar)
\r
6066 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6067 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6068 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6069 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6070 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6071 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6072 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6073 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6074 gameMode == Training) {
\r
6075 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6076 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6077 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6078 FreeProcInstance(lpProc);
\r
6082 /*---------------------------------------------------------------------------*\
\r
6084 * Type-in name dialog functions
\r
6086 \*---------------------------------------------------------------------------*/
\r
6089 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6091 char move[MSG_SIZ];
\r
6094 switch (message) {
\r
6095 case WM_INITDIALOG:
\r
6096 move[0] = (char) lParam;
\r
6097 move[1] = NULLCHAR;
\r
6098 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6099 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6100 SetWindowText(hInput, move);
\r
6102 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6106 switch (LOWORD(wParam)) {
\r
6108 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6109 appData.userName = strdup(move);
\r
6112 EndDialog(hDlg, TRUE);
\r
6115 EndDialog(hDlg, FALSE);
\r
6126 PopUpNameDialog(char firstchar)
\r
6130 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6131 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6132 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6133 FreeProcInstance(lpProc);
\r
6136 /*---------------------------------------------------------------------------*\
\r
6140 \*---------------------------------------------------------------------------*/
\r
6142 /* Nonmodal error box */
\r
6143 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6144 WPARAM wParam, LPARAM lParam);
\r
6147 ErrorPopUp(char *title, char *content)
\r
6151 BOOLEAN modal = hwndMain == NULL;
\r
6169 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6170 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6173 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6175 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6176 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6177 hwndMain, (DLGPROC)lpProc);
\r
6178 FreeProcInstance(lpProc);
\r
6185 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6186 if (errorDialog == NULL) return;
\r
6187 DestroyWindow(errorDialog);
\r
6188 errorDialog = NULL;
\r
6189 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6193 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6198 switch (message) {
\r
6199 case WM_INITDIALOG:
\r
6200 GetWindowRect(hDlg, &rChild);
\r
6203 SetWindowPos(hDlg, NULL, rChild.left,
\r
6204 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6205 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6209 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6210 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6211 and it doesn't work when you resize the dialog.
\r
6212 For now, just give it a default position.
\r
6214 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6216 errorDialog = hDlg;
\r
6217 SetWindowText(hDlg, errorTitle);
\r
6218 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6219 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6223 switch (LOWORD(wParam)) {
\r
6226 if (errorDialog == hDlg) errorDialog = NULL;
\r
6227 DestroyWindow(hDlg);
\r
6239 HWND gothicDialog = NULL;
\r
6242 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6246 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6248 switch (message) {
\r
6249 case WM_INITDIALOG:
\r
6250 GetWindowRect(hDlg, &rChild);
\r
6252 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6256 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6257 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6258 and it doesn't work when you resize the dialog.
\r
6259 For now, just give it a default position.
\r
6261 gothicDialog = hDlg;
\r
6262 SetWindowText(hDlg, errorTitle);
\r
6263 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6264 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6268 switch (LOWORD(wParam)) {
\r
6271 if (errorDialog == hDlg) errorDialog = NULL;
\r
6272 DestroyWindow(hDlg);
\r
6284 GothicPopUp(char *title, VariantClass variant)
\r
6287 static char *lastTitle;
\r
6289 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6290 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6292 if(lastTitle != title && gothicDialog != NULL) {
\r
6293 DestroyWindow(gothicDialog);
\r
6294 gothicDialog = NULL;
\r
6296 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6297 title = lastTitle;
\r
6298 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6299 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6300 hwndMain, (DLGPROC)lpProc);
\r
6301 FreeProcInstance(lpProc);
\r
6306 /*---------------------------------------------------------------------------*\
\r
6308 * Ics Interaction console functions
\r
6310 \*---------------------------------------------------------------------------*/
\r
6312 #define HISTORY_SIZE 64
\r
6313 static char *history[HISTORY_SIZE];
\r
6314 int histIn = 0, histP = 0;
\r
6317 SaveInHistory(char *cmd)
\r
6319 if (history[histIn] != NULL) {
\r
6320 free(history[histIn]);
\r
6321 history[histIn] = NULL;
\r
6323 if (*cmd == NULLCHAR) return;
\r
6324 history[histIn] = StrSave(cmd);
\r
6325 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6326 if (history[histIn] != NULL) {
\r
6327 free(history[histIn]);
\r
6328 history[histIn] = NULL;
\r
6334 PrevInHistory(char *cmd)
\r
6337 if (histP == histIn) {
\r
6338 if (history[histIn] != NULL) free(history[histIn]);
\r
6339 history[histIn] = StrSave(cmd);
\r
6341 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6342 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6344 return history[histP];
\r
6350 if (histP == histIn) return NULL;
\r
6351 histP = (histP + 1) % HISTORY_SIZE;
\r
6352 return history[histP];
\r
6356 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6360 hmenu = LoadMenu(hInst, "TextMenu");
\r
6361 h = GetSubMenu(hmenu, 0);
\r
6363 if (strcmp(e->item, "-") == 0) {
\r
6364 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6365 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6366 int flags = MF_STRING, j = 0;
\r
6367 if (e->item[0] == '|') {
\r
6368 flags |= MF_MENUBARBREAK;
\r
6371 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6372 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6380 WNDPROC consoleTextWindowProc;
\r
6383 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6385 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6386 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6390 SetWindowText(hInput, command);
\r
6392 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6394 sel.cpMin = 999999;
\r
6395 sel.cpMax = 999999;
\r
6396 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6401 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6402 if (sel.cpMin == sel.cpMax) {
\r
6403 /* Expand to surrounding word */
\r
6406 tr.chrg.cpMax = sel.cpMin;
\r
6407 tr.chrg.cpMin = --sel.cpMin;
\r
6408 if (sel.cpMin < 0) break;
\r
6409 tr.lpstrText = name;
\r
6410 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6411 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6415 tr.chrg.cpMin = sel.cpMax;
\r
6416 tr.chrg.cpMax = ++sel.cpMax;
\r
6417 tr.lpstrText = name;
\r
6418 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6419 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6422 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6423 MessageBeep(MB_ICONEXCLAMATION);
\r
6427 tr.lpstrText = name;
\r
6428 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6430 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6431 MessageBeep(MB_ICONEXCLAMATION);
\r
6434 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6437 sprintf(buf, "%s %s", command, name);
\r
6438 SetWindowText(hInput, buf);
\r
6439 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6441 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6442 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6443 SetWindowText(hInput, buf);
\r
6444 sel.cpMin = 999999;
\r
6445 sel.cpMax = 999999;
\r
6446 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6452 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6457 switch (message) {
\r
6459 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6462 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6465 sel.cpMin = 999999;
\r
6466 sel.cpMax = 999999;
\r
6467 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6468 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6473 if(wParam != '\022') {
\r
6474 if (wParam == '\t') {
\r
6475 if (GetKeyState(VK_SHIFT) < 0) {
\r
6477 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6478 if (buttonDesc[0].hwnd) {
\r
6479 SetFocus(buttonDesc[0].hwnd);
\r
6481 SetFocus(hwndMain);
\r
6485 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6488 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6489 JAWS_DELETE( SetFocus(hInput); )
\r
6490 SendMessage(hInput, message, wParam, lParam);
\r
6493 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6494 case WM_RBUTTONDOWN:
\r
6495 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6496 /* Move selection here if it was empty */
\r
6498 pt.x = LOWORD(lParam);
\r
6499 pt.y = HIWORD(lParam);
\r
6500 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6501 if (sel.cpMin == sel.cpMax) {
\r
6502 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6503 sel.cpMax = sel.cpMin;
\r
6504 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6506 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6507 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6509 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6510 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6511 if (sel.cpMin == sel.cpMax) {
\r
6512 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6513 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6515 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6516 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6518 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6519 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6520 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6521 MenuPopup(hwnd, pt, hmenu, -1);
\r
6525 case WM_RBUTTONUP:
\r
6526 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6527 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6528 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6532 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6534 return SendMessage(hInput, message, wParam, lParam);
\r
6535 case WM_MBUTTONDOWN:
\r
6536 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6538 switch (LOWORD(wParam)) {
\r
6539 case IDM_QuickPaste:
\r
6541 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6542 if (sel.cpMin == sel.cpMax) {
\r
6543 MessageBeep(MB_ICONEXCLAMATION);
\r
6546 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6547 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6548 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6553 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6556 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6559 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6563 int i = LOWORD(wParam) - IDM_CommandX;
\r
6564 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6565 icsTextMenuEntry[i].command != NULL) {
\r
6566 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6567 icsTextMenuEntry[i].getname,
\r
6568 icsTextMenuEntry[i].immediate);
\r
6576 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6579 WNDPROC consoleInputWindowProc;
\r
6582 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6584 char buf[MSG_SIZ];
\r
6586 static BOOL sendNextChar = FALSE;
\r
6587 static BOOL quoteNextChar = FALSE;
\r
6588 InputSource *is = consoleInputSource;
\r
6592 switch (message) {
\r
6594 if (!appData.localLineEditing || sendNextChar) {
\r
6595 is->buf[0] = (CHAR) wParam;
\r
6597 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6598 sendNextChar = FALSE;
\r
6601 if (quoteNextChar) {
\r
6602 buf[0] = (char) wParam;
\r
6603 buf[1] = NULLCHAR;
\r
6604 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6605 quoteNextChar = FALSE;
\r
6609 case '\r': /* Enter key */
\r
6610 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6611 if (consoleEcho) SaveInHistory(is->buf);
\r
6612 is->buf[is->count++] = '\n';
\r
6613 is->buf[is->count] = NULLCHAR;
\r
6614 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6615 if (consoleEcho) {
\r
6616 ConsoleOutput(is->buf, is->count, TRUE);
\r
6617 } else if (appData.localLineEditing) {
\r
6618 ConsoleOutput("\n", 1, TRUE);
\r
6621 case '\033': /* Escape key */
\r
6622 SetWindowText(hwnd, "");
\r
6623 cf.cbSize = sizeof(CHARFORMAT);
\r
6624 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6625 if (consoleEcho) {
\r
6626 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6628 cf.crTextColor = COLOR_ECHOOFF;
\r
6630 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6631 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6633 case '\t': /* Tab key */
\r
6634 if (GetKeyState(VK_SHIFT) < 0) {
\r
6636 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6639 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6640 if (buttonDesc[0].hwnd) {
\r
6641 SetFocus(buttonDesc[0].hwnd);
\r
6643 SetFocus(hwndMain);
\r
6647 case '\023': /* Ctrl+S */
\r
6648 sendNextChar = TRUE;
\r
6650 case '\021': /* Ctrl+Q */
\r
6651 quoteNextChar = TRUE;
\r
6661 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6662 p = PrevInHistory(buf);
\r
6664 SetWindowText(hwnd, p);
\r
6665 sel.cpMin = 999999;
\r
6666 sel.cpMax = 999999;
\r
6667 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6672 p = NextInHistory();
\r
6674 SetWindowText(hwnd, p);
\r
6675 sel.cpMin = 999999;
\r
6676 sel.cpMax = 999999;
\r
6677 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6683 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6687 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6691 case WM_MBUTTONDOWN:
\r
6692 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6693 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6695 case WM_RBUTTONUP:
\r
6696 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6697 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6698 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6702 hmenu = LoadMenu(hInst, "InputMenu");
\r
6703 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6704 if (sel.cpMin == sel.cpMax) {
\r
6705 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6706 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6708 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6709 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6711 pt.x = LOWORD(lParam);
\r
6712 pt.y = HIWORD(lParam);
\r
6713 MenuPopup(hwnd, pt, hmenu, -1);
\r
6717 switch (LOWORD(wParam)) {
\r
6719 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6721 case IDM_SelectAll:
\r
6723 sel.cpMax = -1; /*999999?*/
\r
6724 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6727 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6730 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6733 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6738 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6741 #define CO_MAX 100000
\r
6742 #define CO_TRIM 1000
\r
6745 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6747 static SnapData sd;
\r
6748 HWND hText, hInput;
\r
6750 static int sizeX, sizeY;
\r
6751 int newSizeX, newSizeY;
\r
6755 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6756 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6758 switch (message) {
\r
6760 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6762 ENLINK *pLink = (ENLINK*)lParam;
\r
6763 if (pLink->msg == WM_LBUTTONUP)
\r
6767 tr.chrg = pLink->chrg;
\r
6768 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6769 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6770 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6771 free(tr.lpstrText);
\r
6775 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6776 hwndConsole = hDlg;
\r
6778 consoleTextWindowProc = (WNDPROC)
\r
6779 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6780 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6781 consoleInputWindowProc = (WNDPROC)
\r
6782 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6783 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6784 Colorize(ColorNormal, TRUE);
\r
6785 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6786 ChangedConsoleFont();
\r
6787 GetClientRect(hDlg, &rect);
\r
6788 sizeX = rect.right;
\r
6789 sizeY = rect.bottom;
\r
6790 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6791 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6792 WINDOWPLACEMENT wp;
\r
6793 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6794 wp.length = sizeof(WINDOWPLACEMENT);
\r
6796 wp.showCmd = SW_SHOW;
\r
6797 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6798 wp.rcNormalPosition.left = wpConsole.x;
\r
6799 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6800 wp.rcNormalPosition.top = wpConsole.y;
\r
6801 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6802 SetWindowPlacement(hDlg, &wp);
\r
6805 // [HGM] Chessknight's change 2004-07-13
\r
6806 else { /* Determine Defaults */
\r
6807 WINDOWPLACEMENT wp;
\r
6808 wpConsole.x = wpMain.width + 1;
\r
6809 wpConsole.y = wpMain.y;
\r
6810 wpConsole.width = screenWidth - wpMain.width;
\r
6811 wpConsole.height = wpMain.height;
\r
6812 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6813 wp.length = sizeof(WINDOWPLACEMENT);
\r
6815 wp.showCmd = SW_SHOW;
\r
6816 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6817 wp.rcNormalPosition.left = wpConsole.x;
\r
6818 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6819 wp.rcNormalPosition.top = wpConsole.y;
\r
6820 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6821 SetWindowPlacement(hDlg, &wp);
\r
6824 // Allow hText to highlight URLs and send notifications on them
\r
6825 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6826 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6827 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6828 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6842 if (IsIconic(hDlg)) break;
\r
6843 newSizeX = LOWORD(lParam);
\r
6844 newSizeY = HIWORD(lParam);
\r
6845 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6846 RECT rectText, rectInput;
\r
6848 int newTextHeight, newTextWidth;
\r
6849 GetWindowRect(hText, &rectText);
\r
6850 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6851 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6852 if (newTextHeight < 0) {
\r
6853 newSizeY += -newTextHeight;
\r
6854 newTextHeight = 0;
\r
6856 SetWindowPos(hText, NULL, 0, 0,
\r
6857 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6858 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6859 pt.x = rectInput.left;
\r
6860 pt.y = rectInput.top + newSizeY - sizeY;
\r
6861 ScreenToClient(hDlg, &pt);
\r
6862 SetWindowPos(hInput, NULL,
\r
6863 pt.x, pt.y, /* needs client coords */
\r
6864 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6865 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6871 case WM_GETMINMAXINFO:
\r
6872 /* Prevent resizing window too small */
\r
6873 mmi = (MINMAXINFO *) lParam;
\r
6874 mmi->ptMinTrackSize.x = 100;
\r
6875 mmi->ptMinTrackSize.y = 100;
\r
6878 /* [AS] Snapping */
\r
6879 case WM_ENTERSIZEMOVE:
\r
6880 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6883 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6886 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6888 case WM_EXITSIZEMOVE:
\r
6889 UpdateICSWidth(hText);
\r
6890 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6893 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6901 if (hwndConsole) return;
\r
6902 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6903 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6908 ConsoleOutput(char* data, int length, int forceVisible)
\r
6913 char buf[CO_MAX+1];
\r
6916 static int delayLF = 0;
\r
6917 CHARRANGE savesel, sel;
\r
6919 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6927 while (length--) {
\r
6935 } else if (*p == '\007') {
\r
6936 MyPlaySound(&sounds[(int)SoundBell]);
\r
6943 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6944 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6945 /* Save current selection */
\r
6946 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6947 exlen = GetWindowTextLength(hText);
\r
6948 /* Find out whether current end of text is visible */
\r
6949 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6950 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6951 /* Trim existing text if it's too long */
\r
6952 if (exlen + (q - buf) > CO_MAX) {
\r
6953 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6956 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6957 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6959 savesel.cpMin -= trim;
\r
6960 savesel.cpMax -= trim;
\r
6961 if (exlen < 0) exlen = 0;
\r
6962 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6963 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6965 /* Append the new text */
\r
6966 sel.cpMin = exlen;
\r
6967 sel.cpMax = exlen;
\r
6968 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6969 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6970 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6971 if (forceVisible || exlen == 0 ||
\r
6972 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6973 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6974 /* Scroll to make new end of text visible if old end of text
\r
6975 was visible or new text is an echo of user typein */
\r
6976 sel.cpMin = 9999999;
\r
6977 sel.cpMax = 9999999;
\r
6978 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6979 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6980 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6981 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6983 if (savesel.cpMax == exlen || forceVisible) {
\r
6984 /* Move insert point to new end of text if it was at the old
\r
6985 end of text or if the new text is an echo of user typein */
\r
6986 sel.cpMin = 9999999;
\r
6987 sel.cpMax = 9999999;
\r
6988 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6990 /* Restore previous selection */
\r
6991 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6993 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
7000 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
7004 COLORREF oldFg, oldBg;
\r
7008 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
7010 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7011 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7012 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7015 rect.right = x + squareSize;
\r
7017 rect.bottom = y + squareSize;
\r
7020 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
7021 + (rightAlign ? (squareSize*2)/3 : 0),
\r
7022 y, ETO_CLIPPED|ETO_OPAQUE,
\r
7023 &rect, str, strlen(str), NULL);
\r
7025 (void) SetTextColor(hdc, oldFg);
\r
7026 (void) SetBkColor(hdc, oldBg);
\r
7027 (void) SelectObject(hdc, oldFont);
\r
7031 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
7032 RECT *rect, char *color, char *flagFell)
\r
7036 COLORREF oldFg, oldBg;
\r
7039 if (appData.clockMode) {
\r
7041 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7043 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7050 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7051 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7053 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7054 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7056 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7060 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7061 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7062 rect, str, strlen(str), NULL);
\r
7063 if(logoHeight > 0 && appData.clockMode) {
\r
7065 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7066 r.top = rect->top + logoHeight/2;
\r
7067 r.left = rect->left;
\r
7068 r.right = rect->right;
\r
7069 r.bottom = rect->bottom;
\r
7070 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7071 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7072 &r, str, strlen(str), NULL);
\r
7074 (void) SetTextColor(hdc, oldFg);
\r
7075 (void) SetBkColor(hdc, oldBg);
\r
7076 (void) SelectObject(hdc, oldFont);
\r
7081 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7087 if( count <= 0 ) {
\r
7088 if (appData.debugMode) {
\r
7089 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7092 return ERROR_INVALID_USER_BUFFER;
\r
7095 ResetEvent(ovl->hEvent);
\r
7096 ovl->Offset = ovl->OffsetHigh = 0;
\r
7097 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7101 err = GetLastError();
\r
7102 if (err == ERROR_IO_PENDING) {
\r
7103 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7107 err = GetLastError();
\r
7114 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7119 ResetEvent(ovl->hEvent);
\r
7120 ovl->Offset = ovl->OffsetHigh = 0;
\r
7121 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7125 err = GetLastError();
\r
7126 if (err == ERROR_IO_PENDING) {
\r
7127 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7131 err = GetLastError();
\r
7137 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7138 void CheckForInputBufferFull( InputSource * is )
\r
7140 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7141 /* Look for end of line */
\r
7142 char * p = is->buf;
\r
7144 while( p < is->next && *p != '\n' ) {
\r
7148 if( p >= is->next ) {
\r
7149 if (appData.debugMode) {
\r
7150 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7153 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7154 is->count = (DWORD) -1;
\r
7155 is->next = is->buf;
\r
7161 InputThread(LPVOID arg)
\r
7166 is = (InputSource *) arg;
\r
7167 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7168 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7169 while (is->hThread != NULL) {
\r
7170 is->error = DoReadFile(is->hFile, is->next,
\r
7171 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7172 &is->count, &ovl);
\r
7173 if (is->error == NO_ERROR) {
\r
7174 is->next += is->count;
\r
7176 if (is->error == ERROR_BROKEN_PIPE) {
\r
7177 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7180 is->count = (DWORD) -1;
\r
7181 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7186 CheckForInputBufferFull( is );
\r
7188 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7190 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7192 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7195 CloseHandle(ovl.hEvent);
\r
7196 CloseHandle(is->hFile);
\r
7198 if (appData.debugMode) {
\r
7199 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7206 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7208 NonOvlInputThread(LPVOID arg)
\r
7215 is = (InputSource *) arg;
\r
7216 while (is->hThread != NULL) {
\r
7217 is->error = ReadFile(is->hFile, is->next,
\r
7218 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7219 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7220 if (is->error == NO_ERROR) {
\r
7221 /* Change CRLF to LF */
\r
7222 if (is->next > is->buf) {
\r
7224 i = is->count + 1;
\r
7232 if (prev == '\r' && *p == '\n') {
\r
7244 if (is->error == ERROR_BROKEN_PIPE) {
\r
7245 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7248 is->count = (DWORD) -1;
\r
7252 CheckForInputBufferFull( is );
\r
7254 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7256 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7258 if (is->count < 0) break; /* Quit on error */
\r
7260 CloseHandle(is->hFile);
\r
7265 SocketInputThread(LPVOID arg)
\r
7269 is = (InputSource *) arg;
\r
7270 while (is->hThread != NULL) {
\r
7271 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7272 if ((int)is->count == SOCKET_ERROR) {
\r
7273 is->count = (DWORD) -1;
\r
7274 is->error = WSAGetLastError();
\r
7276 is->error = NO_ERROR;
\r
7277 is->next += is->count;
\r
7278 if (is->count == 0 && is->second == is) {
\r
7279 /* End of file on stderr; quit with no message */
\r
7283 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7285 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7287 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7293 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7297 is = (InputSource *) lParam;
\r
7298 if (is->lineByLine) {
\r
7299 /* Feed in lines one by one */
\r
7300 char *p = is->buf;
\r
7302 while (q < is->next) {
\r
7303 if (*q++ == '\n') {
\r
7304 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7309 /* Move any partial line to the start of the buffer */
\r
7311 while (p < is->next) {
\r
7316 if (is->error != NO_ERROR || is->count == 0) {
\r
7317 /* Notify backend of the error. Note: If there was a partial
\r
7318 line at the end, it is not flushed through. */
\r
7319 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7322 /* Feed in the whole chunk of input at once */
\r
7323 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7324 is->next = is->buf;
\r
7328 /*---------------------------------------------------------------------------*\
\r
7330 * Menu enables. Used when setting various modes.
\r
7332 \*---------------------------------------------------------------------------*/
\r
7340 GreyRevert(Boolean grey)
\r
7341 { // [HGM] vari: for retracting variations in local mode
\r
7342 HMENU hmenu = GetMenu(hwndMain);
\r
7343 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7344 EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7348 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7350 while (enab->item > 0) {
\r
7351 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7356 Enables gnuEnables[] = {
\r
7357 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7358 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7359 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7360 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7361 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7362 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7363 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7364 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7365 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7366 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7367 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7368 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7369 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7373 Enables icsEnables[] = {
\r
7374 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7375 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7376 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7377 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7378 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7379 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7380 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7381 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7382 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7383 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7384 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7385 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7386 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7387 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7388 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7389 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7394 Enables zippyEnables[] = {
\r
7395 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7396 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7397 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7398 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7403 Enables ncpEnables[] = {
\r
7404 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7405 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7406 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7407 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7408 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7409 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7410 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7411 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7412 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7413 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7414 { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },
\r
7415 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7416 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7417 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7418 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7419 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7420 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7421 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7422 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7423 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7427 Enables trainingOnEnables[] = {
\r
7428 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7429 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7439 Enables trainingOffEnables[] = {
\r
7440 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7441 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7442 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7443 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7444 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7445 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7446 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7447 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7451 /* These modify either ncpEnables or gnuEnables */
\r
7452 Enables cmailEnables[] = {
\r
7453 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7454 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7455 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7456 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7457 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7458 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7459 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7463 Enables machineThinkingEnables[] = {
\r
7464 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7465 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7466 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7467 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7468 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7469 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7470 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7471 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7472 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7473 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7474 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7475 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7476 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7477 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7478 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7482 Enables userThinkingEnables[] = {
\r
7483 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7484 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7485 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7486 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7487 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7488 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7489 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7490 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7491 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7492 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7493 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7494 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7495 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7496 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7497 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7501 /*---------------------------------------------------------------------------*\
\r
7503 * Front-end interface functions exported by XBoard.
\r
7504 * Functions appear in same order as prototypes in frontend.h.
\r
7506 \*---------------------------------------------------------------------------*/
\r
7510 static UINT prevChecked = 0;
\r
7511 static int prevPausing = 0;
\r
7514 if (pausing != prevPausing) {
\r
7515 prevPausing = pausing;
\r
7516 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7517 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7518 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7521 switch (gameMode) {
\r
7522 case BeginningOfGame:
\r
7523 if (appData.icsActive)
\r
7524 nowChecked = IDM_IcsClient;
\r
7525 else if (appData.noChessProgram)
\r
7526 nowChecked = IDM_EditGame;
\r
7528 nowChecked = IDM_MachineBlack;
\r
7530 case MachinePlaysBlack:
\r
7531 nowChecked = IDM_MachineBlack;
\r
7533 case MachinePlaysWhite:
\r
7534 nowChecked = IDM_MachineWhite;
\r
7536 case TwoMachinesPlay:
\r
7537 nowChecked = IDM_TwoMachines;
\r
7540 nowChecked = IDM_AnalysisMode;
\r
7543 nowChecked = IDM_AnalyzeFile;
\r
7546 nowChecked = IDM_EditGame;
\r
7548 case PlayFromGameFile:
\r
7549 nowChecked = IDM_LoadGame;
\r
7551 case EditPosition:
\r
7552 nowChecked = IDM_EditPosition;
\r
7555 nowChecked = IDM_Training;
\r
7557 case IcsPlayingWhite:
\r
7558 case IcsPlayingBlack:
\r
7559 case IcsObserving:
\r
7561 nowChecked = IDM_IcsClient;
\r
7568 if (prevChecked != 0)
\r
7569 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7570 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7571 if (nowChecked != 0)
\r
7572 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7573 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7575 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7576 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7577 MF_BYCOMMAND|MF_ENABLED);
\r
7579 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7580 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7583 prevChecked = nowChecked;
\r
7585 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7586 if (appData.icsActive) {
\r
7587 if (appData.icsEngineAnalyze) {
\r
7588 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7589 MF_BYCOMMAND|MF_CHECKED);
\r
7591 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7592 MF_BYCOMMAND|MF_UNCHECKED);
\r
7600 HMENU hmenu = GetMenu(hwndMain);
\r
7601 SetMenuEnables(hmenu, icsEnables);
\r
7602 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7603 MF_BYPOSITION|MF_ENABLED);
\r
7605 if (appData.zippyPlay) {
\r
7606 SetMenuEnables(hmenu, zippyEnables);
\r
7607 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7608 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7609 MF_BYCOMMAND|MF_ENABLED);
\r
7617 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7623 HMENU hmenu = GetMenu(hwndMain);
\r
7624 SetMenuEnables(hmenu, ncpEnables);
\r
7625 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7626 MF_BYPOSITION|MF_GRAYED);
\r
7627 DrawMenuBar(hwndMain);
\r
7633 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7637 SetTrainingModeOn()
\r
7640 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7641 for (i = 0; i < N_BUTTONS; i++) {
\r
7642 if (buttonDesc[i].hwnd != NULL)
\r
7643 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7648 VOID SetTrainingModeOff()
\r
7651 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7652 for (i = 0; i < N_BUTTONS; i++) {
\r
7653 if (buttonDesc[i].hwnd != NULL)
\r
7654 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7660 SetUserThinkingEnables()
\r
7662 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7666 SetMachineThinkingEnables()
\r
7668 HMENU hMenu = GetMenu(hwndMain);
\r
7669 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7671 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7673 if (gameMode == MachinePlaysBlack) {
\r
7674 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7675 } else if (gameMode == MachinePlaysWhite) {
\r
7676 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7677 } else if (gameMode == TwoMachinesPlay) {
\r
7678 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7684 DisplayTitle(char *str)
\r
7686 char title[MSG_SIZ], *host;
\r
7687 if (str[0] != NULLCHAR) {
\r
7688 strcpy(title, str);
\r
7689 } else if (appData.icsActive) {
\r
7690 if (appData.icsCommPort[0] != NULLCHAR)
\r
7693 host = appData.icsHost;
\r
7694 sprintf(title, "%s: %s", szTitle, host);
\r
7695 } else if (appData.noChessProgram) {
\r
7696 strcpy(title, szTitle);
\r
7698 strcpy(title, szTitle);
\r
7699 strcat(title, ": ");
\r
7700 strcat(title, first.tidy);
\r
7702 SetWindowText(hwndMain, title);
\r
7707 DisplayMessage(char *str1, char *str2)
\r
7711 int remain = MESSAGE_TEXT_MAX - 1;
\r
7714 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7715 messageText[0] = NULLCHAR;
\r
7717 len = strlen(str1);
\r
7718 if (len > remain) len = remain;
\r
7719 strncpy(messageText, str1, len);
\r
7720 messageText[len] = NULLCHAR;
\r
7723 if (*str2 && remain >= 2) {
\r
7725 strcat(messageText, " ");
\r
7728 len = strlen(str2);
\r
7729 if (len > remain) len = remain;
\r
7730 strncat(messageText, str2, len);
\r
7732 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7734 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7738 hdc = GetDC(hwndMain);
\r
7739 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7740 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7741 &messageRect, messageText, strlen(messageText), NULL);
\r
7742 (void) SelectObject(hdc, oldFont);
\r
7743 (void) ReleaseDC(hwndMain, hdc);
\r
7747 DisplayError(char *str, int error)
\r
7749 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7755 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7756 NULL, error, LANG_NEUTRAL,
\r
7757 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7759 sprintf(buf, "%s:\n%s", str, buf2);
\r
7761 ErrorMap *em = errmap;
\r
7762 while (em->err != 0 && em->err != error) em++;
\r
7763 if (em->err != 0) {
\r
7764 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7766 sprintf(buf, "%s:\nError code %d", str, error);
\r
7771 ErrorPopUp("Error", buf);
\r
7776 DisplayMoveError(char *str)
\r
7778 fromX = fromY = -1;
\r
7779 ClearHighlights();
\r
7780 DrawPosition(FALSE, NULL);
\r
7781 if (appData.popupMoveErrors) {
\r
7782 ErrorPopUp("Error", str);
\r
7784 DisplayMessage(str, "");
\r
7785 moveErrorMessageUp = TRUE;
\r
7790 DisplayFatalError(char *str, int error, int exitStatus)
\r
7792 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7794 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7797 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7798 NULL, error, LANG_NEUTRAL,
\r
7799 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7801 sprintf(buf, "%s:\n%s", str, buf2);
\r
7803 ErrorMap *em = errmap;
\r
7804 while (em->err != 0 && em->err != error) em++;
\r
7805 if (em->err != 0) {
\r
7806 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7808 sprintf(buf, "%s:\nError code %d", str, error);
\r
7813 if (appData.debugMode) {
\r
7814 fprintf(debugFP, "%s: %s\n", label, str);
\r
7816 if (appData.popupExitMessage) {
\r
7817 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7818 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7820 ExitEvent(exitStatus);
\r
7825 DisplayInformation(char *str)
\r
7827 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7832 DisplayNote(char *str)
\r
7834 ErrorPopUp("Note", str);
\r
7839 char *title, *question, *replyPrefix;
\r
7844 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7846 static QuestionParams *qp;
\r
7847 char reply[MSG_SIZ];
\r
7850 switch (message) {
\r
7851 case WM_INITDIALOG:
\r
7852 qp = (QuestionParams *) lParam;
\r
7853 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7854 SetWindowText(hDlg, qp->title);
\r
7855 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7856 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7860 switch (LOWORD(wParam)) {
\r
7862 strcpy(reply, qp->replyPrefix);
\r
7863 if (*reply) strcat(reply, " ");
\r
7864 len = strlen(reply);
\r
7865 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7866 strcat(reply, "\n");
\r
7867 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7868 EndDialog(hDlg, TRUE);
\r
7869 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7872 EndDialog(hDlg, FALSE);
\r
7883 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7885 QuestionParams qp;
\r
7889 qp.question = question;
\r
7890 qp.replyPrefix = replyPrefix;
\r
7892 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7893 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7894 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7895 FreeProcInstance(lpProc);
\r
7898 /* [AS] Pick FRC position */
\r
7899 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7901 static int * lpIndexFRC;
\r
7907 case WM_INITDIALOG:
\r
7908 lpIndexFRC = (int *) lParam;
\r
7910 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7912 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7913 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7914 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7915 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7920 switch( LOWORD(wParam) ) {
\r
7922 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7923 EndDialog( hDlg, 0 );
\r
7924 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7927 EndDialog( hDlg, 1 );
\r
7929 case IDC_NFG_Edit:
\r
7930 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7931 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7933 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7936 case IDC_NFG_Random:
\r
7937 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7938 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7951 int index = appData.defaultFrcPosition;
\r
7952 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7954 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7956 if( result == 0 ) {
\r
7957 appData.defaultFrcPosition = index;
\r
7963 /* [AS] Game list options. Refactored by HGM */
\r
7965 HWND gameListOptionsDialog;
\r
7967 // low-level front-end: clear text edit / list widget
\r
7971 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7974 // low-level front-end: clear text edit / list widget
\r
7976 GLT_DeSelectList()
\r
7978 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7981 // low-level front-end: append line to text edit / list widget
\r
7983 GLT_AddToList( char *name )
\r
7986 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
7990 // low-level front-end: get line from text edit / list widget
\r
7992 GLT_GetFromList( int index, char *name )
\r
7995 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
8001 void GLT_MoveSelection( HWND hDlg, int delta )
\r
8003 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
8004 int idx2 = idx1 + delta;
\r
8005 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
8007 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
8010 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
8011 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
8012 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
8013 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
8017 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
8021 case WM_INITDIALOG:
\r
8022 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
8024 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
8026 /* Initialize list */
\r
8027 GLT_TagsToList( lpUserGLT );
\r
8029 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
8034 switch( LOWORD(wParam) ) {
\r
8037 EndDialog( hDlg, 0 );
\r
8040 EndDialog( hDlg, 1 );
\r
8043 case IDC_GLT_Default:
\r
8044 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8047 case IDC_GLT_Restore:
\r
8048 GLT_TagsToList( appData.gameListTags );
\r
8052 GLT_MoveSelection( hDlg, -1 );
\r
8055 case IDC_GLT_Down:
\r
8056 GLT_MoveSelection( hDlg, +1 );
\r
8066 int GameListOptions()
\r
8069 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8071 strcpy( lpUserGLT, appData.gameListTags );
\r
8073 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8075 if( result == 0 ) {
\r
8076 /* [AS] Memory leak here! */
\r
8077 appData.gameListTags = strdup( lpUserGLT );
\r
8084 DisplayIcsInteractionTitle(char *str)
\r
8086 char consoleTitle[MSG_SIZ];
\r
8088 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8089 SetWindowText(hwndConsole, consoleTitle);
\r
8093 DrawPosition(int fullRedraw, Board board)
\r
8095 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8098 void NotifyFrontendLogin()
\r
8101 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8107 fromX = fromY = -1;
\r
8108 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8109 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8110 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8111 dragInfo.lastpos = dragInfo.pos;
\r
8112 dragInfo.start.x = dragInfo.start.y = -1;
\r
8113 dragInfo.from = dragInfo.start;
\r
8115 DrawPosition(TRUE, NULL);
\r
8121 CommentPopUp(char *title, char *str)
\r
8123 HWND hwnd = GetActiveWindow();
\r
8124 EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0
\r
8126 SetActiveWindow(hwnd);
\r
8130 CommentPopDown(void)
\r
8132 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8133 if (commentDialog) {
\r
8134 ShowWindow(commentDialog, SW_HIDE);
\r
8136 commentUp = FALSE;
\r
8140 EditCommentPopUp(int index, char *title, char *str)
\r
8142 EitherCommentPopUp(index, title, str, TRUE);
\r
8149 MyPlaySound(&sounds[(int)SoundMove]);
\r
8152 VOID PlayIcsWinSound()
\r
8154 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8157 VOID PlayIcsLossSound()
\r
8159 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8162 VOID PlayIcsDrawSound()
\r
8164 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8167 VOID PlayIcsUnfinishedSound()
\r
8169 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8175 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8183 consoleEcho = TRUE;
\r
8184 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8185 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8186 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8195 consoleEcho = FALSE;
\r
8196 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8197 /* This works OK: set text and background both to the same color */
\r
8199 cf.crTextColor = COLOR_ECHOOFF;
\r
8200 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8201 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8204 /* No Raw()...? */
\r
8206 void Colorize(ColorClass cc, int continuation)
\r
8208 currentColorClass = cc;
\r
8209 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8210 consoleCF.crTextColor = textAttribs[cc].color;
\r
8211 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8212 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8218 static char buf[MSG_SIZ];
\r
8219 DWORD bufsiz = MSG_SIZ;
\r
8221 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8222 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8224 if (!GetUserName(buf, &bufsiz)) {
\r
8225 /*DisplayError("Error getting user name", GetLastError());*/
\r
8226 strcpy(buf, "User");
\r
8234 static char buf[MSG_SIZ];
\r
8235 DWORD bufsiz = MSG_SIZ;
\r
8237 if (!GetComputerName(buf, &bufsiz)) {
\r
8238 /*DisplayError("Error getting host name", GetLastError());*/
\r
8239 strcpy(buf, "Unknown");
\r
8246 ClockTimerRunning()
\r
8248 return clockTimerEvent != 0;
\r
8254 if (clockTimerEvent == 0) return FALSE;
\r
8255 KillTimer(hwndMain, clockTimerEvent);
\r
8256 clockTimerEvent = 0;
\r
8261 StartClockTimer(long millisec)
\r
8263 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8264 (UINT) millisec, NULL);
\r
8268 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8271 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8273 if(appData.noGUI) return;
\r
8274 hdc = GetDC(hwndMain);
\r
8275 if (!IsIconic(hwndMain)) {
\r
8276 DisplayAClock(hdc, timeRemaining, highlight,
\r
8277 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8279 if (highlight && iconCurrent == iconBlack) {
\r
8280 iconCurrent = iconWhite;
\r
8281 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8282 if (IsIconic(hwndMain)) {
\r
8283 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8286 (void) ReleaseDC(hwndMain, hdc);
\r
8288 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8292 DisplayBlackClock(long timeRemaining, int highlight)
\r
8295 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8297 if(appData.noGUI) return;
\r
8298 hdc = GetDC(hwndMain);
\r
8299 if (!IsIconic(hwndMain)) {
\r
8300 DisplayAClock(hdc, timeRemaining, highlight,
\r
8301 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8303 if (highlight && iconCurrent == iconWhite) {
\r
8304 iconCurrent = iconBlack;
\r
8305 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8306 if (IsIconic(hwndMain)) {
\r
8307 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8310 (void) ReleaseDC(hwndMain, hdc);
\r
8312 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8317 LoadGameTimerRunning()
\r
8319 return loadGameTimerEvent != 0;
\r
8323 StopLoadGameTimer()
\r
8325 if (loadGameTimerEvent == 0) return FALSE;
\r
8326 KillTimer(hwndMain, loadGameTimerEvent);
\r
8327 loadGameTimerEvent = 0;
\r
8332 StartLoadGameTimer(long millisec)
\r
8334 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8335 (UINT) millisec, NULL);
\r
8343 char fileTitle[MSG_SIZ];
\r
8345 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8346 f = OpenFileDialog(hwndMain, "a", defName,
\r
8347 appData.oldSaveStyle ? "gam" : "pgn",
\r
8349 "Save Game to File", NULL, fileTitle, NULL);
\r
8351 SaveGame(f, 0, "");
\r
8358 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8360 if (delayedTimerEvent != 0) {
\r
8361 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8362 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8364 KillTimer(hwndMain, delayedTimerEvent);
\r
8365 delayedTimerEvent = 0;
\r
8366 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8367 delayedTimerCallback();
\r
8369 delayedTimerCallback = cb;
\r
8370 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8371 (UINT) millisec, NULL);
\r
8374 DelayedEventCallback
\r
8377 if (delayedTimerEvent) {
\r
8378 return delayedTimerCallback;
\r
8385 CancelDelayedEvent()
\r
8387 if (delayedTimerEvent) {
\r
8388 KillTimer(hwndMain, delayedTimerEvent);
\r
8389 delayedTimerEvent = 0;
\r
8393 DWORD GetWin32Priority(int nice)
\r
8394 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8396 REALTIME_PRIORITY_CLASS 0x00000100
\r
8397 HIGH_PRIORITY_CLASS 0x00000080
\r
8398 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8399 NORMAL_PRIORITY_CLASS 0x00000020
\r
8400 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8401 IDLE_PRIORITY_CLASS 0x00000040
\r
8403 if (nice < -15) return 0x00000080;
\r
8404 if (nice < 0) return 0x00008000;
\r
8405 if (nice == 0) return 0x00000020;
\r
8406 if (nice < 15) return 0x00004000;
\r
8407 return 0x00000040;
\r
8410 /* Start a child process running the given program.
\r
8411 The process's standard output can be read from "from", and its
\r
8412 standard input can be written to "to".
\r
8413 Exit with fatal error if anything goes wrong.
\r
8414 Returns an opaque pointer that can be used to destroy the process
\r
8418 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8420 #define BUFSIZE 4096
\r
8422 HANDLE hChildStdinRd, hChildStdinWr,
\r
8423 hChildStdoutRd, hChildStdoutWr;
\r
8424 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8425 SECURITY_ATTRIBUTES saAttr;
\r
8427 PROCESS_INFORMATION piProcInfo;
\r
8428 STARTUPINFO siStartInfo;
\r
8430 char buf[MSG_SIZ];
\r
8433 if (appData.debugMode) {
\r
8434 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8439 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8440 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8441 saAttr.bInheritHandle = TRUE;
\r
8442 saAttr.lpSecurityDescriptor = NULL;
\r
8445 * The steps for redirecting child's STDOUT:
\r
8446 * 1. Create anonymous pipe to be STDOUT for child.
\r
8447 * 2. Create a noninheritable duplicate of read handle,
\r
8448 * and close the inheritable read handle.
\r
8451 /* Create a pipe for the child's STDOUT. */
\r
8452 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8453 return GetLastError();
\r
8456 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8457 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8458 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8459 FALSE, /* not inherited */
\r
8460 DUPLICATE_SAME_ACCESS);
\r
8462 return GetLastError();
\r
8464 CloseHandle(hChildStdoutRd);
\r
8467 * The steps for redirecting child's STDIN:
\r
8468 * 1. Create anonymous pipe to be STDIN for child.
\r
8469 * 2. Create a noninheritable duplicate of write handle,
\r
8470 * and close the inheritable write handle.
\r
8473 /* Create a pipe for the child's STDIN. */
\r
8474 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8475 return GetLastError();
\r
8478 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8479 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8480 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8481 FALSE, /* not inherited */
\r
8482 DUPLICATE_SAME_ACCESS);
\r
8484 return GetLastError();
\r
8486 CloseHandle(hChildStdinWr);
\r
8488 /* Arrange to (1) look in dir for the child .exe file, and
\r
8489 * (2) have dir be the child's working directory. Interpret
\r
8490 * dir relative to the directory WinBoard loaded from. */
\r
8491 GetCurrentDirectory(MSG_SIZ, buf);
\r
8492 SetCurrentDirectory(installDir);
\r
8493 SetCurrentDirectory(dir);
\r
8495 /* Now create the child process. */
\r
8497 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8498 siStartInfo.lpReserved = NULL;
\r
8499 siStartInfo.lpDesktop = NULL;
\r
8500 siStartInfo.lpTitle = NULL;
\r
8501 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8502 siStartInfo.cbReserved2 = 0;
\r
8503 siStartInfo.lpReserved2 = NULL;
\r
8504 siStartInfo.hStdInput = hChildStdinRd;
\r
8505 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8506 siStartInfo.hStdError = hChildStdoutWr;
\r
8508 fSuccess = CreateProcess(NULL,
\r
8509 cmdLine, /* command line */
\r
8510 NULL, /* process security attributes */
\r
8511 NULL, /* primary thread security attrs */
\r
8512 TRUE, /* handles are inherited */
\r
8513 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8514 NULL, /* use parent's environment */
\r
8516 &siStartInfo, /* STARTUPINFO pointer */
\r
8517 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8519 err = GetLastError();
\r
8520 SetCurrentDirectory(buf); /* return to prev directory */
\r
8525 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8526 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8527 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8530 /* Close the handles we don't need in the parent */
\r
8531 CloseHandle(piProcInfo.hThread);
\r
8532 CloseHandle(hChildStdinRd);
\r
8533 CloseHandle(hChildStdoutWr);
\r
8535 /* Prepare return value */
\r
8536 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8537 cp->kind = CPReal;
\r
8538 cp->hProcess = piProcInfo.hProcess;
\r
8539 cp->pid = piProcInfo.dwProcessId;
\r
8540 cp->hFrom = hChildStdoutRdDup;
\r
8541 cp->hTo = hChildStdinWrDup;
\r
8543 *pr = (void *) cp;
\r
8545 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8546 2000 where engines sometimes don't see the initial command(s)
\r
8547 from WinBoard and hang. I don't understand how that can happen,
\r
8548 but the Sleep is harmless, so I've put it in. Others have also
\r
8549 reported what may be the same problem, so hopefully this will fix
\r
8550 it for them too. */
\r
8558 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8560 ChildProc *cp; int result;
\r
8562 cp = (ChildProc *) pr;
\r
8563 if (cp == NULL) return;
\r
8565 switch (cp->kind) {
\r
8567 /* TerminateProcess is considered harmful, so... */
\r
8568 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8569 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8570 /* The following doesn't work because the chess program
\r
8571 doesn't "have the same console" as WinBoard. Maybe
\r
8572 we could arrange for this even though neither WinBoard
\r
8573 nor the chess program uses a console for stdio? */
\r
8574 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8576 /* [AS] Special termination modes for misbehaving programs... */
\r
8577 if( signal == 9 ) {
\r
8578 result = TerminateProcess( cp->hProcess, 0 );
\r
8580 if ( appData.debugMode) {
\r
8581 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8584 else if( signal == 10 ) {
\r
8585 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8587 if( dw != WAIT_OBJECT_0 ) {
\r
8588 result = TerminateProcess( cp->hProcess, 0 );
\r
8590 if ( appData.debugMode) {
\r
8591 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8597 CloseHandle(cp->hProcess);
\r
8601 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8605 closesocket(cp->sock);
\r
8610 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8611 closesocket(cp->sock);
\r
8612 closesocket(cp->sock2);
\r
8620 InterruptChildProcess(ProcRef pr)
\r
8624 cp = (ChildProc *) pr;
\r
8625 if (cp == NULL) return;
\r
8626 switch (cp->kind) {
\r
8628 /* The following doesn't work because the chess program
\r
8629 doesn't "have the same console" as WinBoard. Maybe
\r
8630 we could arrange for this even though neither WinBoard
\r
8631 nor the chess program uses a console for stdio */
\r
8632 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8637 /* Can't interrupt */
\r
8641 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8648 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8650 char cmdLine[MSG_SIZ];
\r
8652 if (port[0] == NULLCHAR) {
\r
8653 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8655 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8657 return StartChildProcess(cmdLine, "", pr);
\r
8661 /* Code to open TCP sockets */
\r
8664 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8669 struct sockaddr_in sa, mysa;
\r
8670 struct hostent FAR *hp;
\r
8671 unsigned short uport;
\r
8672 WORD wVersionRequested;
\r
8675 /* Initialize socket DLL */
\r
8676 wVersionRequested = MAKEWORD(1, 1);
\r
8677 err = WSAStartup(wVersionRequested, &wsaData);
\r
8678 if (err != 0) return err;
\r
8681 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8682 err = WSAGetLastError();
\r
8687 /* Bind local address using (mostly) don't-care values.
\r
8689 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8690 mysa.sin_family = AF_INET;
\r
8691 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8692 uport = (unsigned short) 0;
\r
8693 mysa.sin_port = htons(uport);
\r
8694 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8695 == SOCKET_ERROR) {
\r
8696 err = WSAGetLastError();
\r
8701 /* Resolve remote host name */
\r
8702 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8703 if (!(hp = gethostbyname(host))) {
\r
8704 unsigned int b0, b1, b2, b3;
\r
8706 err = WSAGetLastError();
\r
8708 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8709 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8710 hp->h_addrtype = AF_INET;
\r
8712 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8713 hp->h_addr_list[0] = (char *) malloc(4);
\r
8714 hp->h_addr_list[0][0] = (char) b0;
\r
8715 hp->h_addr_list[0][1] = (char) b1;
\r
8716 hp->h_addr_list[0][2] = (char) b2;
\r
8717 hp->h_addr_list[0][3] = (char) b3;
\r
8723 sa.sin_family = hp->h_addrtype;
\r
8724 uport = (unsigned short) atoi(port);
\r
8725 sa.sin_port = htons(uport);
\r
8726 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8728 /* Make connection */
\r
8729 if (connect(s, (struct sockaddr *) &sa,
\r
8730 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8731 err = WSAGetLastError();
\r
8736 /* Prepare return value */
\r
8737 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8738 cp->kind = CPSock;
\r
8740 *pr = (ProcRef *) cp;
\r
8746 OpenCommPort(char *name, ProcRef *pr)
\r
8751 char fullname[MSG_SIZ];
\r
8753 if (*name != '\\')
\r
8754 sprintf(fullname, "\\\\.\\%s", name);
\r
8756 strcpy(fullname, name);
\r
8758 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8759 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8760 if (h == (HANDLE) -1) {
\r
8761 return GetLastError();
\r
8765 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8767 /* Accumulate characters until a 100ms pause, then parse */
\r
8768 ct.ReadIntervalTimeout = 100;
\r
8769 ct.ReadTotalTimeoutMultiplier = 0;
\r
8770 ct.ReadTotalTimeoutConstant = 0;
\r
8771 ct.WriteTotalTimeoutMultiplier = 0;
\r
8772 ct.WriteTotalTimeoutConstant = 0;
\r
8773 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8775 /* Prepare return value */
\r
8776 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8777 cp->kind = CPComm;
\r
8780 *pr = (ProcRef *) cp;
\r
8786 OpenLoopback(ProcRef *pr)
\r
8788 DisplayFatalError("Not implemented", 0, 1);
\r
8794 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8799 struct sockaddr_in sa, mysa;
\r
8800 struct hostent FAR *hp;
\r
8801 unsigned short uport;
\r
8802 WORD wVersionRequested;
\r
8805 char stderrPortStr[MSG_SIZ];
\r
8807 /* Initialize socket DLL */
\r
8808 wVersionRequested = MAKEWORD(1, 1);
\r
8809 err = WSAStartup(wVersionRequested, &wsaData);
\r
8810 if (err != 0) return err;
\r
8812 /* Resolve remote host name */
\r
8813 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8814 if (!(hp = gethostbyname(host))) {
\r
8815 unsigned int b0, b1, b2, b3;
\r
8817 err = WSAGetLastError();
\r
8819 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8820 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8821 hp->h_addrtype = AF_INET;
\r
8823 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8824 hp->h_addr_list[0] = (char *) malloc(4);
\r
8825 hp->h_addr_list[0][0] = (char) b0;
\r
8826 hp->h_addr_list[0][1] = (char) b1;
\r
8827 hp->h_addr_list[0][2] = (char) b2;
\r
8828 hp->h_addr_list[0][3] = (char) b3;
\r
8834 sa.sin_family = hp->h_addrtype;
\r
8835 uport = (unsigned short) 514;
\r
8836 sa.sin_port = htons(uport);
\r
8837 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8839 /* Bind local socket to unused "privileged" port address
\r
8841 s = INVALID_SOCKET;
\r
8842 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8843 mysa.sin_family = AF_INET;
\r
8844 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8845 for (fromPort = 1023;; fromPort--) {
\r
8846 if (fromPort < 0) {
\r
8848 return WSAEADDRINUSE;
\r
8850 if (s == INVALID_SOCKET) {
\r
8851 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8852 err = WSAGetLastError();
\r
8857 uport = (unsigned short) fromPort;
\r
8858 mysa.sin_port = htons(uport);
\r
8859 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8860 == SOCKET_ERROR) {
\r
8861 err = WSAGetLastError();
\r
8862 if (err == WSAEADDRINUSE) continue;
\r
8866 if (connect(s, (struct sockaddr *) &sa,
\r
8867 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8868 err = WSAGetLastError();
\r
8869 if (err == WSAEADDRINUSE) {
\r
8880 /* Bind stderr local socket to unused "privileged" port address
\r
8882 s2 = INVALID_SOCKET;
\r
8883 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8884 mysa.sin_family = AF_INET;
\r
8885 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8886 for (fromPort = 1023;; fromPort--) {
\r
8887 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8888 if (fromPort < 0) {
\r
8889 (void) closesocket(s);
\r
8891 return WSAEADDRINUSE;
\r
8893 if (s2 == INVALID_SOCKET) {
\r
8894 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8895 err = WSAGetLastError();
\r
8901 uport = (unsigned short) fromPort;
\r
8902 mysa.sin_port = htons(uport);
\r
8903 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8904 == SOCKET_ERROR) {
\r
8905 err = WSAGetLastError();
\r
8906 if (err == WSAEADDRINUSE) continue;
\r
8907 (void) closesocket(s);
\r
8911 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8912 err = WSAGetLastError();
\r
8913 if (err == WSAEADDRINUSE) {
\r
8915 s2 = INVALID_SOCKET;
\r
8918 (void) closesocket(s);
\r
8919 (void) closesocket(s2);
\r
8925 prevStderrPort = fromPort; // remember port used
\r
8926 sprintf(stderrPortStr, "%d", fromPort);
\r
8928 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8929 err = WSAGetLastError();
\r
8930 (void) closesocket(s);
\r
8931 (void) closesocket(s2);
\r
8936 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8937 err = WSAGetLastError();
\r
8938 (void) closesocket(s);
\r
8939 (void) closesocket(s2);
\r
8943 if (*user == NULLCHAR) user = UserName();
\r
8944 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8945 err = WSAGetLastError();
\r
8946 (void) closesocket(s);
\r
8947 (void) closesocket(s2);
\r
8951 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8952 err = WSAGetLastError();
\r
8953 (void) closesocket(s);
\r
8954 (void) closesocket(s2);
\r
8959 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8960 err = WSAGetLastError();
\r
8961 (void) closesocket(s);
\r
8962 (void) closesocket(s2);
\r
8966 (void) closesocket(s2); /* Stop listening */
\r
8968 /* Prepare return value */
\r
8969 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8970 cp->kind = CPRcmd;
\r
8973 *pr = (ProcRef *) cp;
\r
8980 AddInputSource(ProcRef pr, int lineByLine,
\r
8981 InputCallback func, VOIDSTAR closure)
\r
8983 InputSource *is, *is2 = NULL;
\r
8984 ChildProc *cp = (ChildProc *) pr;
\r
8986 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8987 is->lineByLine = lineByLine;
\r
8989 is->closure = closure;
\r
8990 is->second = NULL;
\r
8991 is->next = is->buf;
\r
8992 if (pr == NoProc) {
\r
8993 is->kind = CPReal;
\r
8994 consoleInputSource = is;
\r
8996 is->kind = cp->kind;
\r
8998 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8999 we create all threads suspended so that the is->hThread variable can be
\r
9000 safely assigned, then let the threads start with ResumeThread.
\r
9002 switch (cp->kind) {
\r
9004 is->hFile = cp->hFrom;
\r
9005 cp->hFrom = NULL; /* now owned by InputThread */
\r
9007 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
9008 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9012 is->hFile = cp->hFrom;
\r
9013 cp->hFrom = NULL; /* now owned by InputThread */
\r
9015 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
9016 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9020 is->sock = cp->sock;
\r
9022 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9023 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9027 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
9029 is->sock = cp->sock;
\r
9031 is2->sock = cp->sock2;
\r
9032 is2->second = is2;
\r
9034 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9035 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
9037 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9038 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9042 if( is->hThread != NULL ) {
\r
9043 ResumeThread( is->hThread );
\r
9046 if( is2 != NULL && is2->hThread != NULL ) {
\r
9047 ResumeThread( is2->hThread );
\r
9051 return (InputSourceRef) is;
\r
9055 RemoveInputSource(InputSourceRef isr)
\r
9059 is = (InputSource *) isr;
\r
9060 is->hThread = NULL; /* tell thread to stop */
\r
9061 CloseHandle(is->hThread);
\r
9062 if (is->second != NULL) {
\r
9063 is->second->hThread = NULL;
\r
9064 CloseHandle(is->second->hThread);
\r
9068 int no_wrap(char *message, int count)
\r
9070 ConsoleOutput(message, count, FALSE);
\r
9075 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9078 int outCount = SOCKET_ERROR;
\r
9079 ChildProc *cp = (ChildProc *) pr;
\r
9080 static OVERLAPPED ovl;
\r
9081 static int line = 0;
\r
9085 if (appData.noJoin || !appData.useInternalWrap)
\r
9086 return no_wrap(message, count);
\r
9089 int width = get_term_width();
\r
9090 int len = wrap(NULL, message, count, width, &line);
\r
9091 char *msg = malloc(len);
\r
9095 return no_wrap(message, count);
\r
9098 dbgchk = wrap(msg, message, count, width, &line);
\r
9099 if (dbgchk != len && appData.debugMode)
\r
9100 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9101 ConsoleOutput(msg, len, FALSE);
\r
9108 if (ovl.hEvent == NULL) {
\r
9109 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9111 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9113 switch (cp->kind) {
\r
9116 outCount = send(cp->sock, message, count, 0);
\r
9117 if (outCount == SOCKET_ERROR) {
\r
9118 *outError = WSAGetLastError();
\r
9120 *outError = NO_ERROR;
\r
9125 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9126 &dOutCount, NULL)) {
\r
9127 *outError = NO_ERROR;
\r
9128 outCount = (int) dOutCount;
\r
9130 *outError = GetLastError();
\r
9135 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9136 &dOutCount, &ovl);
\r
9137 if (*outError == NO_ERROR) {
\r
9138 outCount = (int) dOutCount;
\r
9146 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9149 /* Ignore delay, not implemented for WinBoard */
\r
9150 return OutputToProcess(pr, message, count, outError);
\r
9155 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9156 char *buf, int count, int error)
\r
9158 DisplayFatalError("Not implemented", 0, 1);
\r
9161 /* see wgamelist.c for Game List functions */
\r
9162 /* see wedittags.c for Edit Tags functions */
\r
9169 char buf[MSG_SIZ];
\r
9172 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9173 f = fopen(buf, "r");
\r
9175 ProcessICSInitScript(f);
\r
9183 StartAnalysisClock()
\r
9185 if (analysisTimerEvent) return;
\r
9186 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9187 (UINT) 2000, NULL);
\r
9191 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9193 highlightInfo.sq[0].x = fromX;
\r
9194 highlightInfo.sq[0].y = fromY;
\r
9195 highlightInfo.sq[1].x = toX;
\r
9196 highlightInfo.sq[1].y = toY;
\r
9202 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9203 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9207 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9209 premoveHighlightInfo.sq[0].x = fromX;
\r
9210 premoveHighlightInfo.sq[0].y = fromY;
\r
9211 premoveHighlightInfo.sq[1].x = toX;
\r
9212 premoveHighlightInfo.sq[1].y = toY;
\r
9216 ClearPremoveHighlights()
\r
9218 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9219 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9223 ShutDownFrontEnd()
\r
9225 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9226 DeleteClipboardTempFiles();
\r
9232 if (IsIconic(hwndMain))
\r
9233 ShowWindow(hwndMain, SW_RESTORE);
\r
9235 SetActiveWindow(hwndMain);
\r
9239 * Prototypes for animation support routines
\r
9241 static void ScreenSquare(int column, int row, POINT * pt);
\r
9242 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9243 POINT frames[], int * nFrames);
\r
9247 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9248 { // [HGM] atomic: animate blast wave
\r
9250 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9251 explodeInfo.fromX = fromX;
\r
9252 explodeInfo.fromY = fromY;
\r
9253 explodeInfo.toX = toX;
\r
9254 explodeInfo.toY = toY;
\r
9255 for(i=1; i<nFrames; i++) {
\r
9256 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9257 DrawPosition(FALSE, NULL);
\r
9258 Sleep(appData.animSpeed);
\r
9260 explodeInfo.radius = 0;
\r
9261 DrawPosition(TRUE, NULL);
\r
9267 AnimateMove(board, fromX, fromY, toX, toY)
\r
9274 ChessSquare piece;
\r
9275 POINT start, finish, mid;
\r
9276 POINT frames[kFactor * 2 + 1];
\r
9279 if (!appData.animate) return;
\r
9280 if (doingSizing) return;
\r
9281 if (fromY < 0 || fromX < 0) return;
\r
9282 piece = board[fromY][fromX];
\r
9283 if (piece >= EmptySquare) return;
\r
9285 ScreenSquare(fromX, fromY, &start);
\r
9286 ScreenSquare(toX, toY, &finish);
\r
9288 /* All pieces except knights move in straight line */
\r
9289 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9290 mid.x = start.x + (finish.x - start.x) / 2;
\r
9291 mid.y = start.y + (finish.y - start.y) / 2;
\r
9293 /* Knight: make diagonal movement then straight */
\r
9294 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9295 mid.x = start.x + (finish.x - start.x) / 2;
\r
9299 mid.y = start.y + (finish.y - start.y) / 2;
\r
9303 /* Don't use as many frames for very short moves */
\r
9304 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9305 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9307 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9309 animInfo.from.x = fromX;
\r
9310 animInfo.from.y = fromY;
\r
9311 animInfo.to.x = toX;
\r
9312 animInfo.to.y = toY;
\r
9313 animInfo.lastpos = start;
\r
9314 animInfo.piece = piece;
\r
9315 for (n = 0; n < nFrames; n++) {
\r
9316 animInfo.pos = frames[n];
\r
9317 DrawPosition(FALSE, NULL);
\r
9318 animInfo.lastpos = animInfo.pos;
\r
9319 Sleep(appData.animSpeed);
\r
9321 animInfo.pos = finish;
\r
9322 DrawPosition(FALSE, NULL);
\r
9323 animInfo.piece = EmptySquare;
\r
9324 if(gameInfo.variant == VariantAtomic &&
\r
9325 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9326 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9329 /* Convert board position to corner of screen rect and color */
\r
9332 ScreenSquare(column, row, pt)
\r
9333 int column; int row; POINT * pt;
\r
9336 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9337 pt->y = lineGap + row * (squareSize + lineGap);
\r
9339 pt->x = lineGap + column * (squareSize + lineGap);
\r
9340 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9344 /* Generate a series of frame coords from start->mid->finish.
\r
9345 The movement rate doubles until the half way point is
\r
9346 reached, then halves back down to the final destination,
\r
9347 which gives a nice slow in/out effect. The algorithmn
\r
9348 may seem to generate too many intermediates for short
\r
9349 moves, but remember that the purpose is to attract the
\r
9350 viewers attention to the piece about to be moved and
\r
9351 then to where it ends up. Too few frames would be less
\r
9355 Tween(start, mid, finish, factor, frames, nFrames)
\r
9356 POINT * start; POINT * mid;
\r
9357 POINT * finish; int factor;
\r
9358 POINT frames[]; int * nFrames;
\r
9360 int n, fraction = 1, count = 0;
\r
9362 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9363 for (n = 0; n < factor; n++)
\r
9365 for (n = 0; n < factor; n++) {
\r
9366 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9367 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9369 fraction = fraction / 2;
\r
9373 frames[count] = *mid;
\r
9376 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9378 for (n = 0; n < factor; n++) {
\r
9379 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9380 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9382 fraction = fraction * 2;
\r
9388 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9390 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9392 EvalGraphSet( first, last, current, pvInfoList );
\r