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
872 SetActiveWindow(hwndConsole);
\r
874 if(!appData.noGUI) UpdateWindow(hwnd); else ShowWindow(hwnd, SW_MINIMIZE);
\r
875 if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file
\r
884 HMENU hmenu = GetMenu(hwndMain);
\r
886 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
887 MF_BYCOMMAND|((appData.icsActive &&
\r
888 *appData.icsCommPort != NULLCHAR) ?
\r
889 MF_ENABLED : MF_GRAYED));
\r
890 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
891 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
892 MF_CHECKED : MF_UNCHECKED));
\r
895 //---------------------------------------------------------------------------------------------------------
\r
897 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
\r
898 #define XBOARD FALSE
\r
900 #define OPTCHAR "/"
\r
901 #define SEPCHAR "="
\r
905 // front-end part of option handling
\r
908 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
910 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
911 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
914 lf->lfEscapement = 0;
\r
915 lf->lfOrientation = 0;
\r
916 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
917 lf->lfItalic = mfp->italic;
\r
918 lf->lfUnderline = mfp->underline;
\r
919 lf->lfStrikeOut = mfp->strikeout;
\r
920 lf->lfCharSet = mfp->charset;
\r
921 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
922 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
923 lf->lfQuality = DEFAULT_QUALITY;
\r
924 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
925 strcpy(lf->lfFaceName, mfp->faceName);
\r
929 CreateFontInMF(MyFont *mf)
\r
931 LFfromMFP(&mf->lf, &mf->mfp);
\r
932 if (mf->hf) DeleteObject(mf->hf);
\r
933 mf->hf = CreateFontIndirect(&mf->lf);
\r
936 // [HGM] This platform-dependent table provides the location for storing the color info
\r
938 colorVariable[] = {
\r
943 &highlightSquareColor,
\r
944 &premoveHighlightColor,
\r
946 &consoleBackgroundColor,
\r
947 &appData.fontForeColorWhite,
\r
948 &appData.fontBackColorWhite,
\r
949 &appData.fontForeColorBlack,
\r
950 &appData.fontBackColorBlack,
\r
951 &appData.evalHistColorWhite,
\r
952 &appData.evalHistColorBlack,
\r
953 &appData.highlightArrowColor,
\r
956 /* Command line font name parser. NULL name means do nothing.
\r
957 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
958 For backward compatibility, syntax without the colon is also
\r
959 accepted, but font names with digits in them won't work in that case.
\r
962 ParseFontName(char *name, MyFontParams *mfp)
\r
965 if (name == NULL) return;
\r
967 q = strchr(p, ':');
\r
969 if (q - p >= sizeof(mfp->faceName))
\r
970 ExitArgError("Font name too long:", name);
\r
971 memcpy(mfp->faceName, p, q - p);
\r
972 mfp->faceName[q - p] = NULLCHAR;
\r
976 while (*p && !isdigit(*p)) {
\r
978 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
979 ExitArgError("Font name too long:", name);
\r
981 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
984 if (!*p) ExitArgError("Font point size missing:", name);
\r
985 mfp->pointSize = (float) atof(p);
\r
986 mfp->bold = (strchr(p, 'b') != NULL);
\r
987 mfp->italic = (strchr(p, 'i') != NULL);
\r
988 mfp->underline = (strchr(p, 'u') != NULL);
\r
989 mfp->strikeout = (strchr(p, 's') != NULL);
\r
990 mfp->charset = DEFAULT_CHARSET;
\r
991 q = strchr(p, 'c');
\r
993 mfp->charset = (BYTE) atoi(q+1);
\r
997 ParseFont(char *name, int number)
\r
998 { // wrapper to shield back-end from 'font'
\r
999 ParseFontName(name, &font[boardSize][number]->mfp);
\r
1004 { // in WB we have a 2D array of fonts; this initializes their description
\r
1006 /* Point font array elements to structures and
\r
1007 parse default font names */
\r
1008 for (i=0; i<NUM_FONTS; i++) {
\r
1009 for (j=0; j<NUM_SIZES; j++) {
\r
1010 font[j][i] = &fontRec[j][i];
\r
1011 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1018 { // here we create the actual fonts from the selected descriptions
\r
1020 for (i=0; i<NUM_FONTS; i++) {
\r
1021 for (j=0; j<NUM_SIZES; j++) {
\r
1022 CreateFontInMF(font[j][i]);
\r
1026 /* Color name parser.
\r
1027 X version accepts X color names, but this one
\r
1028 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1030 ParseColorName(char *name)
\r
1032 int red, green, blue, count;
\r
1033 char buf[MSG_SIZ];
\r
1035 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1037 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1038 &red, &green, &blue);
\r
1041 sprintf(buf, "Can't parse color name %s", name);
\r
1042 DisplayError(buf, 0);
\r
1043 return RGB(0, 0, 0);
\r
1045 return PALETTERGB(red, green, blue);
\r
1049 ParseColor(int n, char *name)
\r
1050 { // for WinBoard the color is an int, which needs to be derived from the string
\r
1051 if(colorVariable[n]) *(int*)colorVariable[n] = ParseColorName(name);
\r
1055 ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1057 char *e = argValue;
\r
1061 if (*e == 'b') eff |= CFE_BOLD;
\r
1062 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1063 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1064 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1065 else if (*e == '#' || isdigit(*e)) break;
\r
1069 *color = ParseColorName(e);
\r
1073 ParseTextAttribs(ColorClass cc, char *s)
\r
1074 { // [HGM] front-end wrapper that does the platform-dependent call
\r
1075 // for XBoard we would set (&appData.colorShout)[cc] = strdup(s);
\r
1076 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, s);
\r
1080 ParseBoardSize(void *addr, char *name)
\r
1081 { // [HGM] rewritten with return-value ptr to shield back-end from BoardSize
\r
1082 BoardSize bs = SizeTiny;
\r
1083 while (sizeInfo[bs].name != NULL) {
\r
1084 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) {
\r
1085 *(BoardSize *)addr = bs;
\r
1090 ExitArgError("Unrecognized board size value", name);
\r
1095 { // [HGM] import name from appData first
\r
1098 for (cc = (ColorClass)0; cc < ColorNormal; cc++) {
\r
1099 textAttribs[cc].sound.name = strdup((&appData.soundShout)[cc]);
\r
1100 textAttribs[cc].sound.data = NULL;
\r
1101 MyLoadSound(&textAttribs[cc].sound);
\r
1103 for (cc = ColorNormal; cc < NColorClasses; cc++) {
\r
1104 textAttribs[cc].sound.name = strdup("");
\r
1105 textAttribs[cc].sound.data = NULL;
\r
1107 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1108 sounds[sc].name = strdup((&appData.soundMove)[sc]);
\r
1109 sounds[sc].data = NULL;
\r
1110 MyLoadSound(&sounds[sc]);
\r
1115 SetCommPortDefaults()
\r
1117 memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +
\r
1118 dcb.DCBlength = sizeof(DCB);
\r
1119 dcb.BaudRate = 9600;
\r
1120 dcb.fBinary = TRUE;
\r
1121 dcb.fParity = FALSE;
\r
1122 dcb.fOutxCtsFlow = FALSE;
\r
1123 dcb.fOutxDsrFlow = FALSE;
\r
1124 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1125 dcb.fDsrSensitivity = FALSE;
\r
1126 dcb.fTXContinueOnXoff = TRUE;
\r
1127 dcb.fOutX = FALSE;
\r
1129 dcb.fNull = FALSE;
\r
1130 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1131 dcb.fAbortOnError = FALSE;
\r
1133 dcb.Parity = SPACEPARITY;
\r
1134 dcb.StopBits = ONESTOPBIT;
\r
1137 // [HGM] args: these three cases taken out to stay in front-end
\r
1139 SaveFontArg(FILE *f, ArgDescriptor *ad)
\r
1140 { // in WinBoard every board size has its own font, and the "argLoc" identifies the table,
\r
1141 // while the curent board size determines the element. This system should be ported to XBoard.
\r
1142 // What the table contains pointers to, and how to print the font description, remains platform-dependent
\r
1144 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1145 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1146 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1147 fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",
\r
1148 ad->argName, mfp->faceName, mfp->pointSize,
\r
1149 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1150 mfp->bold ? "b" : "",
\r
1151 mfp->italic ? "i" : "",
\r
1152 mfp->underline ? "u" : "",
\r
1153 mfp->strikeout ? "s" : "",
\r
1154 (int)mfp->charset);
\r
1160 { // [HGM] copy the names from the internal WB variables to appData
\r
1163 for (cc = (ColorClass)0; cc < ColorNormal; cc++)
\r
1164 (&appData.soundShout)[cc] = textAttribs[cc].sound.name;
\r
1165 for (sc = (SoundClass)0; sc < NSoundClasses; sc++)
\r
1166 (&appData.soundMove)[sc] = sounds[sc].name;
\r
1170 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
\r
1171 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
\r
1172 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1173 fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,
\r
1174 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1175 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1176 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1177 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1178 (ta->effects) ? " " : "",
\r
1179 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1183 SaveColor(FILE *f, ArgDescriptor *ad)
\r
1184 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
\r
1185 COLORREF color = *(COLORREF *)colorVariable[(int)ad->argLoc];
\r
1186 fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName,
\r
1187 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1191 SaveBoardSize(FILE *f, char *name, void *addr)
\r
1192 { // wrapper to shield back-end from BoardSize & sizeInfo
\r
1193 fprintf(f, "/%s=%s\n", name, sizeInfo[*(BoardSize *)addr].name);
\r
1197 ParseCommPortSettings(char *s)
\r
1198 { // wrapper to keep dcb from back-end
\r
1199 ParseCommSettings(s, &dcb);
\r
1204 { // wrapper to shield use of window handles from back-end (make addressible by number?)
\r
1205 GetActualPlacement(hwndMain, &wpMain);
\r
1206 GetActualPlacement(hwndConsole, &wpConsole);
\r
1207 GetActualPlacement(commentDialog, &wpComment);
\r
1208 GetActualPlacement(editTagsDialog, &wpTags);
\r
1209 GetActualPlacement(gameListDialog, &wpGameList);
\r
1210 GetActualPlacement(moveHistoryDialog, &wpMoveHistory);
\r
1211 GetActualPlacement(evalGraphDialog, &wpEvalGraph);
\r
1212 GetActualPlacement(engineOutputDialog, &wpEngineOutput);
\r
1216 PrintCommPortSettings(FILE *f, char *name)
\r
1217 { // wrapper to shield back-end from DCB
\r
1218 PrintCommSettings(f, name, &dcb);
\r
1222 MySearchPath(char *installDir, char *name, char *fullname)
\r
1225 return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);
\r
1229 MyGetFullPathName(char *name, char *fullname)
\r
1232 return (int) GetFullPathName(name, MSG_SIZ, fullname, &dummy);
\r
1237 { // [HGM] args: allows testing if main window is realized from back-end
\r
1238 return hwndMain != NULL;
\r
1242 PopUpStartupDialog()
\r
1246 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1247 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1248 FreeProcInstance(lpProc);
\r
1251 /*---------------------------------------------------------------------------*\
\r
1253 * GDI board drawing routines
\r
1255 \*---------------------------------------------------------------------------*/
\r
1257 /* [AS] Draw square using background texture */
\r
1258 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )
\r
1263 return; /* Should never happen! */
\r
1266 SetGraphicsMode( dst, GM_ADVANCED );
\r
1273 /* X reflection */
\r
1278 x.eDx = (FLOAT) dw + dx - 1;
\r
1281 SetWorldTransform( dst, &x );
\r
1284 /* Y reflection */
\r
1290 x.eDy = (FLOAT) dh + dy - 1;
\r
1292 SetWorldTransform( dst, &x );
\r
1300 x.eDx = (FLOAT) dx;
\r
1301 x.eDy = (FLOAT) dy;
\r
1304 SetWorldTransform( dst, &x );
\r
1308 BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );
\r
1316 SetWorldTransform( dst, &x );
\r
1318 ModifyWorldTransform( dst, 0, MWT_IDENTITY );
\r
1321 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */
\r
1323 PM_WP = (int) WhitePawn,
\r
1324 PM_WN = (int) WhiteKnight,
\r
1325 PM_WB = (int) WhiteBishop,
\r
1326 PM_WR = (int) WhiteRook,
\r
1327 PM_WQ = (int) WhiteQueen,
\r
1328 PM_WF = (int) WhiteFerz,
\r
1329 PM_WW = (int) WhiteWazir,
\r
1330 PM_WE = (int) WhiteAlfil,
\r
1331 PM_WM = (int) WhiteMan,
\r
1332 PM_WO = (int) WhiteCannon,
\r
1333 PM_WU = (int) WhiteUnicorn,
\r
1334 PM_WH = (int) WhiteNightrider,
\r
1335 PM_WA = (int) WhiteAngel,
\r
1336 PM_WC = (int) WhiteMarshall,
\r
1337 PM_WAB = (int) WhiteCardinal,
\r
1338 PM_WD = (int) WhiteDragon,
\r
1339 PM_WL = (int) WhiteLance,
\r
1340 PM_WS = (int) WhiteCobra,
\r
1341 PM_WV = (int) WhiteFalcon,
\r
1342 PM_WSG = (int) WhiteSilver,
\r
1343 PM_WG = (int) WhiteGrasshopper,
\r
1344 PM_WK = (int) WhiteKing,
\r
1345 PM_BP = (int) BlackPawn,
\r
1346 PM_BN = (int) BlackKnight,
\r
1347 PM_BB = (int) BlackBishop,
\r
1348 PM_BR = (int) BlackRook,
\r
1349 PM_BQ = (int) BlackQueen,
\r
1350 PM_BF = (int) BlackFerz,
\r
1351 PM_BW = (int) BlackWazir,
\r
1352 PM_BE = (int) BlackAlfil,
\r
1353 PM_BM = (int) BlackMan,
\r
1354 PM_BO = (int) BlackCannon,
\r
1355 PM_BU = (int) BlackUnicorn,
\r
1356 PM_BH = (int) BlackNightrider,
\r
1357 PM_BA = (int) BlackAngel,
\r
1358 PM_BC = (int) BlackMarshall,
\r
1359 PM_BG = (int) BlackGrasshopper,
\r
1360 PM_BAB = (int) BlackCardinal,
\r
1361 PM_BD = (int) BlackDragon,
\r
1362 PM_BL = (int) BlackLance,
\r
1363 PM_BS = (int) BlackCobra,
\r
1364 PM_BV = (int) BlackFalcon,
\r
1365 PM_BSG = (int) BlackSilver,
\r
1366 PM_BK = (int) BlackKing
\r
1369 static HFONT hPieceFont = NULL;
\r
1370 static HBITMAP hPieceMask[(int) EmptySquare];
\r
1371 static HBITMAP hPieceFace[(int) EmptySquare];
\r
1372 static int fontBitmapSquareSize = 0;
\r
1373 static char pieceToFontChar[(int) EmptySquare] =
\r
1374 { 'p', 'n', 'b', 'r', 'q',
\r
1375 'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',
\r
1376 'k', 'o', 'm', 'v', 't', 'w',
\r
1377 'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',
\r
1380 extern BOOL SetCharTable( char *table, const char * map );
\r
1381 /* [HGM] moved to backend.c */
\r
1383 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )
\r
1386 BYTE r1 = GetRValue( color );
\r
1387 BYTE g1 = GetGValue( color );
\r
1388 BYTE b1 = GetBValue( color );
\r
1394 /* Create a uniform background first */
\r
1395 hbrush = CreateSolidBrush( color );
\r
1396 SetRect( &rc, 0, 0, squareSize, squareSize );
\r
1397 FillRect( hdc, &rc, hbrush );
\r
1398 DeleteObject( hbrush );
\r
1401 /* Vertical gradient, good for pawn, knight and rook, less for queen and king */
\r
1402 int steps = squareSize / 2;
\r
1405 for( i=0; i<steps; i++ ) {
\r
1406 BYTE r = r1 - (r1-r2) * i / steps;
\r
1407 BYTE g = g1 - (g1-g2) * i / steps;
\r
1408 BYTE b = b1 - (b1-b2) * i / steps;
\r
1410 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1411 SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );
\r
1412 FillRect( hdc, &rc, hbrush );
\r
1413 DeleteObject(hbrush);
\r
1416 else if( mode == 2 ) {
\r
1417 /* Diagonal gradient, good more or less for every piece */
\r
1418 POINT triangle[3];
\r
1419 HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );
\r
1420 HBRUSH hbrush_old;
\r
1421 int steps = squareSize;
\r
1424 triangle[0].x = squareSize - steps;
\r
1425 triangle[0].y = squareSize;
\r
1426 triangle[1].x = squareSize;
\r
1427 triangle[1].y = squareSize;
\r
1428 triangle[2].x = squareSize;
\r
1429 triangle[2].y = squareSize - steps;
\r
1431 for( i=0; i<steps; i++ ) {
\r
1432 BYTE r = r1 - (r1-r2) * i / steps;
\r
1433 BYTE g = g1 - (g1-g2) * i / steps;
\r
1434 BYTE b = b1 - (b1-b2) * i / steps;
\r
1436 hbrush = CreateSolidBrush( RGB(r,g,b) );
\r
1437 hbrush_old = SelectObject( hdc, hbrush );
\r
1438 Polygon( hdc, triangle, 3 );
\r
1439 SelectObject( hdc, hbrush_old );
\r
1440 DeleteObject(hbrush);
\r
1445 SelectObject( hdc, hpen );
\r
1450 [AS] The method I use to create the bitmaps it a bit tricky, but it
\r
1451 seems to work ok. The main problem here is to find the "inside" of a chess
\r
1452 piece: follow the steps as explained below.
\r
1454 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )
\r
1458 COLORREF chroma = RGB(0xFF,0x00,0xFF);
\r
1462 int backColor = whitePieceColor;
\r
1463 int foreColor = blackPieceColor;
\r
1465 if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {
\r
1466 backColor = appData.fontBackColorWhite;
\r
1467 foreColor = appData.fontForeColorWhite;
\r
1469 else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {
\r
1470 backColor = appData.fontBackColorBlack;
\r
1471 foreColor = appData.fontForeColorBlack;
\r
1475 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1477 hbm_old = SelectObject( hdc, hbm );
\r
1481 rc.right = squareSize;
\r
1482 rc.bottom = squareSize;
\r
1484 /* Step 1: background is now black */
\r
1485 FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );
\r
1487 GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );
\r
1489 pt.x = (squareSize - sz.cx) / 2;
\r
1490 pt.y = (squareSize - sz.cy) / 2;
\r
1492 SetBkMode( hdc, TRANSPARENT );
\r
1493 SetTextColor( hdc, chroma );
\r
1494 /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */
\r
1495 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1497 SelectObject( hdc, GetStockObject(WHITE_BRUSH) );
\r
1498 /* Step 3: the area outside the piece is filled with white */
\r
1499 // FloodFill( hdc, 0, 0, chroma );
\r
1500 ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );
\r
1501 ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big
\r
1502 ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );
\r
1503 ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );
\r
1504 SelectObject( hdc, GetStockObject(BLACK_BRUSH) );
\r
1506 Step 4: this is the tricky part, the area inside the piece is filled with black,
\r
1507 but if the start point is not inside the piece we're lost!
\r
1508 There should be a better way to do this... if we could create a region or path
\r
1509 from the fill operation we would be fine for example.
\r
1511 // FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );
\r
1512 ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );
\r
1514 { /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */
\r
1515 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1516 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1518 SelectObject( dc2, bm2 );
\r
1519 BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy
\r
1520 BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1521 BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1522 BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1523 BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );
\r
1526 DeleteObject( bm2 );
\r
1529 SetTextColor( hdc, 0 );
\r
1531 Step 5: some fonts have "disconnected" areas that are skipped by the fill:
\r
1532 draw the piece again in black for safety.
\r
1534 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1536 SelectObject( hdc, hbm_old );
\r
1538 if( hPieceMask[index] != NULL ) {
\r
1539 DeleteObject( hPieceMask[index] );
\r
1542 hPieceMask[index] = hbm;
\r
1545 hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1547 SelectObject( hdc, hbm );
\r
1550 HDC dc1 = CreateCompatibleDC( hdc_window );
\r
1551 HDC dc2 = CreateCompatibleDC( hdc_window );
\r
1552 HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );
\r
1554 SelectObject( dc1, hPieceMask[index] );
\r
1555 SelectObject( dc2, bm2 );
\r
1556 FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );
\r
1557 BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );
\r
1560 Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves
\r
1561 the piece background and deletes (makes transparent) the rest.
\r
1562 Thanks to that mask, we are free to paint the background with the greates
\r
1563 freedom, as we'll be able to mask off the unwanted parts when finished.
\r
1564 We use this, to make gradients and give the pieces a "roundish" look.
\r
1566 SetPieceBackground( hdc, backColor, 2 );
\r
1567 BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );
\r
1571 DeleteObject( bm2 );
\r
1574 SetTextColor( hdc, foreColor );
\r
1575 TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );
\r
1577 SelectObject( hdc, hbm_old );
\r
1579 if( hPieceFace[index] != NULL ) {
\r
1580 DeleteObject( hPieceFace[index] );
\r
1583 hPieceFace[index] = hbm;
\r
1586 static int TranslatePieceToFontPiece( int piece )
\r
1616 case BlackMarshall:
\r
1620 case BlackNightrider:
\r
1626 case BlackUnicorn:
\r
1630 case BlackGrasshopper:
\r
1642 case BlackCardinal:
\r
1649 case WhiteMarshall:
\r
1653 case WhiteNightrider:
\r
1659 case WhiteUnicorn:
\r
1663 case WhiteGrasshopper:
\r
1675 case WhiteCardinal:
\r
1684 void CreatePiecesFromFont()
\r
1687 HDC hdc_window = NULL;
\r
1693 if( fontBitmapSquareSize < 0 ) {
\r
1694 /* Something went seriously wrong in the past: do not try to recreate fonts! */
\r
1698 if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {
\r
1699 fontBitmapSquareSize = -1;
\r
1703 if( fontBitmapSquareSize != squareSize ) {
\r
1704 hdc_window = GetDC( hwndMain );
\r
1705 hdc = CreateCompatibleDC( hdc_window );
\r
1707 if( hPieceFont != NULL ) {
\r
1708 DeleteObject( hPieceFont );
\r
1711 for( i=0; i<=(int)BlackKing; i++ ) {
\r
1712 hPieceMask[i] = NULL;
\r
1713 hPieceFace[i] = NULL;
\r
1719 if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {
\r
1720 fontHeight = appData.fontPieceSize;
\r
1723 fontHeight = (fontHeight * squareSize) / 100;
\r
1725 lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );
\r
1727 lf.lfEscapement = 0;
\r
1728 lf.lfOrientation = 0;
\r
1729 lf.lfWeight = FW_NORMAL;
\r
1731 lf.lfUnderline = 0;
\r
1732 lf.lfStrikeOut = 0;
\r
1733 lf.lfCharSet = DEFAULT_CHARSET;
\r
1734 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1735 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1736 lf.lfQuality = PROOF_QUALITY;
\r
1737 lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
\r
1738 strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );
\r
1739 lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';
\r
1741 hPieceFont = CreateFontIndirect( &lf );
\r
1743 if( hPieceFont == NULL ) {
\r
1744 fontBitmapSquareSize = -2;
\r
1747 /* Setup font-to-piece character table */
\r
1748 if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {
\r
1749 /* No (or wrong) global settings, try to detect the font */
\r
1750 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {
\r
1752 SetCharTable(pieceToFontChar, "phbrqkojntwl");
\r
1754 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {
\r
1755 /* DiagramTT* family */
\r
1756 SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");
\r
1758 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {
\r
1759 /* Fairy symbols */
\r
1760 SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
\r
1762 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {
\r
1763 /* Good Companion (Some characters get warped as literal :-( */
\r
1764 char s[] = "1cmWG0??S??oYI23wgQU";
\r
1765 s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;
\r
1766 SetCharTable(pieceToFontChar, s);
\r
1769 /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */
\r
1770 SetCharTable(pieceToFontChar, "pnbrqkomvtwl");
\r
1774 /* Create bitmaps */
\r
1775 hfont_old = SelectObject( hdc, hPieceFont );
\r
1776 for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */
\r
1777 if(PieceToChar((ChessSquare)i) != '.') /* skip unused pieces */
\r
1778 CreatePieceMaskFromFont( hdc_window, hdc, i );
\r
1780 SelectObject( hdc, hfont_old );
\r
1782 fontBitmapSquareSize = squareSize;
\r
1786 if( hdc != NULL ) {
\r
1790 if( hdc_window != NULL ) {
\r
1791 ReleaseDC( hwndMain, hdc_window );
\r
1796 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1800 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1801 if (gameInfo.event &&
\r
1802 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1803 strcmp(name, "k80s") == 0) {
\r
1804 strcpy(name, "tim");
\r
1806 return LoadBitmap(hinst, name);
\r
1810 /* Insert a color into the program's logical palette
\r
1811 structure. This code assumes the given color is
\r
1812 the result of the RGB or PALETTERGB macro, and it
\r
1813 knows how those macros work (which is documented).
\r
1816 InsertInPalette(COLORREF color)
\r
1818 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1820 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1821 DisplayFatalError("Too many colors", 0, 1);
\r
1822 pLogPal->palNumEntries--;
\r
1826 pe->peFlags = (char) 0;
\r
1827 pe->peRed = (char) (0xFF & color);
\r
1828 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1829 pe->peBlue = (char) (0xFF & (color >> 16));
\r
1835 InitDrawingColors()
\r
1837 if (pLogPal == NULL) {
\r
1838 /* Allocate enough memory for a logical palette with
\r
1839 * PALETTESIZE entries and set the size and version fields
\r
1840 * of the logical palette structure.
\r
1842 pLogPal = (NPLOGPALETTE)
\r
1843 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
1844 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
1845 pLogPal->palVersion = 0x300;
\r
1847 pLogPal->palNumEntries = 0;
\r
1849 InsertInPalette(lightSquareColor);
\r
1850 InsertInPalette(darkSquareColor);
\r
1851 InsertInPalette(whitePieceColor);
\r
1852 InsertInPalette(blackPieceColor);
\r
1853 InsertInPalette(highlightSquareColor);
\r
1854 InsertInPalette(premoveHighlightColor);
\r
1856 /* create a logical color palette according the information
\r
1857 * in the LOGPALETTE structure.
\r
1859 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
1861 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
1862 blackSquareBrush = CreateSolidBrush(blackPieceColor);
\r
1863 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
1864 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
1865 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
1866 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
1867 explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic
\r
1868 markerBrush = CreateSolidBrush(premoveHighlightColor); // [HGM] markers
\r
1869 /* [AS] Force rendering of the font-based pieces */
\r
1870 if( fontBitmapSquareSize > 0 ) {
\r
1871 fontBitmapSquareSize = 0;
\r
1877 BoardWidth(int boardSize, int n)
\r
1878 { /* [HGM] argument n added to allow different width and height */
\r
1879 int lineGap = sizeInfo[boardSize].lineGap;
\r
1881 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1882 lineGap = appData.overrideLineGap;
\r
1885 return (n + 1) * lineGap +
\r
1886 n * sizeInfo[boardSize].squareSize;
\r
1889 /* Respond to board resize by dragging edge */
\r
1891 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
1893 BoardSize newSize = NUM_SIZES - 1;
\r
1894 static int recurse = 0;
\r
1895 if (IsIconic(hwndMain)) return;
\r
1896 if (recurse > 0) return;
\r
1898 while (newSize > 0) {
\r
1899 InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects
\r
1900 if(newSizeX >= sizeInfo[newSize].cliWidth &&
\r
1901 newSizeY >= sizeInfo[newSize].cliHeight) break;
\r
1904 boardSize = newSize;
\r
1905 InitDrawingSizes(boardSize, flags);
\r
1912 InitDrawingSizes(BoardSize boardSize, int flags)
\r
1914 int i, boardWidth, boardHeight; /* [HGM] height treated separately */
\r
1915 ChessSquare piece;
\r
1916 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
1918 SIZE clockSize, messageSize;
\r
1920 char buf[MSG_SIZ];
\r
1922 HMENU hmenu = GetMenu(hwndMain);
\r
1923 RECT crect, wrect, oldRect;
\r
1925 LOGBRUSH logbrush;
\r
1927 int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only
\r
1928 if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }
\r
1930 /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */
\r
1931 if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;
\r
1933 oldRect.left = wpMain.x; //[HGM] placement: remember previous window params
\r
1934 oldRect.top = wpMain.y;
\r
1935 oldRect.right = wpMain.x + wpMain.width;
\r
1936 oldRect.bottom = wpMain.y + wpMain.height;
\r
1938 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
1939 smallLayout = sizeInfo[boardSize].smallLayout;
\r
1940 squareSize = sizeInfo[boardSize].squareSize;
\r
1941 lineGap = sizeInfo[boardSize].lineGap;
\r
1942 minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted */
\r
1944 if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {
\r
1945 lineGap = appData.overrideLineGap;
\r
1948 if (tinyLayout != oldTinyLayout) {
\r
1949 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
1951 style &= ~WS_SYSMENU;
\r
1952 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
1953 "&Minimize\tCtrl+F4");
\r
1955 style |= WS_SYSMENU;
\r
1956 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
1958 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
1960 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
1961 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
1962 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
1964 DrawMenuBar(hwndMain);
\r
1967 boardWidth = BoardWidth(boardSize, BOARD_WIDTH);
\r
1968 boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);
\r
1970 /* Get text area sizes */
\r
1971 hdc = GetDC(hwndMain);
\r
1972 if (appData.clockMode) {
\r
1973 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
1975 sprintf(buf, "White");
\r
1977 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
1978 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
1979 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
1980 str = "We only care about the height here";
\r
1981 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
1982 SelectObject(hdc, oldFont);
\r
1983 ReleaseDC(hwndMain, hdc);
\r
1985 /* Compute where everything goes */
\r
1986 if((first.programLogo || second.programLogo) && !tinyLayout) {
\r
1987 /* [HGM] logo: if either logo is on, reserve space for it */
\r
1988 logoHeight = 2*clockSize.cy;
\r
1989 leftLogoRect.left = OUTER_MARGIN;
\r
1990 leftLogoRect.right = leftLogoRect.left + 4*clockSize.cy;
\r
1991 leftLogoRect.top = OUTER_MARGIN;
\r
1992 leftLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
1994 rightLogoRect.right = OUTER_MARGIN + boardWidth;
\r
1995 rightLogoRect.left = rightLogoRect.right - 4*clockSize.cy;
\r
1996 rightLogoRect.top = OUTER_MARGIN;
\r
1997 rightLogoRect.bottom = OUTER_MARGIN + logoHeight;
\r
2000 whiteRect.left = leftLogoRect.right;
\r
2001 whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;
\r
2002 whiteRect.top = OUTER_MARGIN;
\r
2003 whiteRect.bottom = whiteRect.top + logoHeight;
\r
2005 blackRect.right = rightLogoRect.left;
\r
2006 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2007 blackRect.top = whiteRect.top;
\r
2008 blackRect.bottom = whiteRect.bottom;
\r
2010 whiteRect.left = OUTER_MARGIN;
\r
2011 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2012 whiteRect.top = OUTER_MARGIN;
\r
2013 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2015 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2016 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2017 blackRect.top = whiteRect.top;
\r
2018 blackRect.bottom = whiteRect.bottom;
\r
2020 logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!
\r
2023 messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;
\r
2024 if (appData.showButtonBar) {
\r
2025 messageRect.right = OUTER_MARGIN + boardWidth // [HGM] logo: expressed independent of clock placement
\r
2026 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2028 messageRect.right = OUTER_MARGIN + boardWidth;
\r
2030 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2031 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2033 boardRect.left = OUTER_MARGIN;
\r
2034 boardRect.right = boardRect.left + boardWidth;
\r
2035 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2036 boardRect.bottom = boardRect.top + boardHeight;
\r
2038 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2039 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2040 oldBoardSize = boardSize;
\r
2041 oldTinyLayout = tinyLayout;
\r
2042 winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2043 winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2044 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2045 if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only
\r
2046 wpMain.width = winW; // [HGM] placement: set through temporary which can used by initial sizing choice
\r
2047 wpMain.height = winH; // without disturbing window attachments
\r
2048 GetWindowRect(hwndMain, &wrect);
\r
2049 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2050 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2052 // [HGM] placement: let attached windows follow size change.
\r
2053 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, moveHistoryDialog, &wpMoveHistory );
\r
2054 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, evalGraphDialog, &wpEvalGraph );
\r
2055 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, engineOutputDialog, &wpEngineOutput );
\r
2056 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, gameListDialog, &wpGameList );
\r
2057 ReattachAfterSize( &oldRect, wpMain.width, wpMain.height, hwndConsole, &wpConsole );
\r
2059 /* compensate if menu bar wrapped */
\r
2060 GetClientRect(hwndMain, &crect);
\r
2061 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2062 wpMain.height += offby;
\r
2064 case WMSZ_TOPLEFT:
\r
2065 SetWindowPos(hwndMain, NULL,
\r
2066 wrect.right - wpMain.width, wrect.bottom - wpMain.height,
\r
2067 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2070 case WMSZ_TOPRIGHT:
\r
2072 SetWindowPos(hwndMain, NULL,
\r
2073 wrect.left, wrect.bottom - wpMain.height,
\r
2074 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2077 case WMSZ_BOTTOMLEFT:
\r
2079 SetWindowPos(hwndMain, NULL,
\r
2080 wrect.right - wpMain.width, wrect.top,
\r
2081 wpMain.width, wpMain.height, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2084 case WMSZ_BOTTOMRIGHT:
\r
2088 SetWindowPos(hwndMain, NULL, 0, 0, wpMain.width, wpMain.height,
\r
2089 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2094 for (i = 0; i < N_BUTTONS; i++) {
\r
2095 if (buttonDesc[i].hwnd != NULL) {
\r
2096 DestroyWindow(buttonDesc[i].hwnd);
\r
2097 buttonDesc[i].hwnd = NULL;
\r
2099 if (appData.showButtonBar) {
\r
2100 buttonDesc[i].hwnd =
\r
2101 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2102 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2103 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2104 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2105 (HMENU) buttonDesc[i].id,
\r
2106 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2108 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2109 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2110 MAKELPARAM(FALSE, 0));
\r
2112 if (buttonDesc[i].id == IDM_Pause)
\r
2113 hwndPause = buttonDesc[i].hwnd;
\r
2114 buttonDesc[i].wndproc = (WNDPROC)
\r
2115 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2118 if (gridPen != NULL) DeleteObject(gridPen);
\r
2119 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2120 if (premovePen != NULL) DeleteObject(premovePen);
\r
2121 if (lineGap != 0) {
\r
2122 logbrush.lbStyle = BS_SOLID;
\r
2123 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2125 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2126 lineGap, &logbrush, 0, NULL);
\r
2127 logbrush.lbColor = highlightSquareColor;
\r
2129 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2130 lineGap, &logbrush, 0, NULL);
\r
2132 logbrush.lbColor = premoveHighlightColor;
\r
2134 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2135 lineGap, &logbrush, 0, NULL);
\r
2137 /* [HGM] Loop had to be split in part for vert. and hor. lines */
\r
2138 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
\r
2139 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2140 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2141 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2142 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2143 BOARD_WIDTH * (squareSize + lineGap);
\r
2144 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2146 for (i = 0; i < BOARD_WIDTH + 1; i++) {
\r
2147 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;
\r
2148 gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =
\r
2149 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +
\r
2150 lineGap / 2 + (i * (squareSize + lineGap));
\r
2151 gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =
\r
2152 boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);
\r
2153 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2157 /* [HGM] Licensing requirement */
\r
2159 if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else
\r
2162 if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else
\r
2164 GothicPopUp( "", VariantNormal);
\r
2167 /* if (boardSize == oldBoardSize) return; [HGM] variant might have changed */
\r
2169 /* Load piece bitmaps for this board size */
\r
2170 for (i=0; i<=2; i++) {
\r
2171 for (piece = WhitePawn;
\r
2172 (int) piece < (int) BlackPawn;
\r
2173 piece = (ChessSquare) ((int) piece + 1)) {
\r
2174 if (pieceBitmap[i][piece] != NULL)
\r
2175 DeleteObject(pieceBitmap[i][piece]);
\r
2179 fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */
\r
2180 // Orthodox Chess pieces
\r
2181 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2182 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2183 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2184 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2185 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2186 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2187 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2188 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2189 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2190 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2191 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2192 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2193 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2194 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2195 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2196 if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {
\r
2197 // in Shogi, Hijack the unused Queen for Lance
\r
2198 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2199 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2200 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2202 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2203 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2204 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2207 if(squareSize <= 72 && squareSize >= 33) {
\r
2208 /* A & C are available in most sizes now */
\r
2209 if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like
\r
2210 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2211 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2212 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2213 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2214 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2215 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2216 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2217 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2218 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2219 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2220 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2221 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2222 } else { // Smirf-like
\r
2223 pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");
\r
2224 pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");
\r
2225 pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");
\r
2227 if(gameInfo.variant == VariantGothic) { // Vortex-like
\r
2228 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2229 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2230 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2231 } else { // WinBoard standard
\r
2232 pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");
\r
2233 pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");
\r
2234 pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");
\r
2239 if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */
\r
2240 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");
\r
2241 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");
\r
2242 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");
\r
2243 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");
\r
2244 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");
\r
2245 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2246 pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");
\r
2247 pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");
\r
2248 pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");
\r
2249 pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");
\r
2250 pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");
\r
2251 pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");
\r
2252 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");
\r
2253 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");
\r
2254 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");
\r
2255 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");
\r
2256 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");
\r
2257 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");
\r
2258 pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");
\r
2259 pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");
\r
2260 pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");
\r
2261 pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");
\r
2262 pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");
\r
2263 pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");
\r
2264 pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");
\r
2265 pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");
\r
2266 pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");
\r
2267 pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");
\r
2268 pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");
\r
2269 pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");
\r
2271 if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */
\r
2272 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");
\r
2273 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");
\r
2274 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2275 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");
\r
2276 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");
\r
2277 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2278 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");
\r
2279 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");
\r
2280 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2281 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");
\r
2282 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");
\r
2283 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");
\r
2285 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");
\r
2286 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");
\r
2287 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");
\r
2288 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");
\r
2289 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");
\r
2290 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");
\r
2291 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");
\r
2292 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");
\r
2293 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");
\r
2294 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");
\r
2295 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");
\r
2296 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");
\r
2299 } else { /* other size, no special bitmaps available. Use smaller symbols */
\r
2300 if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;
\r
2301 else minorSize = sizeInfo[(int)boardSize - 2].squareSize;
\r
2302 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");
\r
2303 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");
\r
2304 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");
\r
2305 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "s");
\r
2306 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "o");
\r
2307 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "b", minorSize, "w");
\r
2308 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "s");
\r
2309 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "o");
\r
2310 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "r", minorSize, "w");
\r
2311 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");
\r
2312 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");
\r
2313 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");
\r
2317 if(gameInfo.variant == VariantShogi && squareSize == 58)
\r
2318 /* special Shogi support in this size */
\r
2319 { for (i=0; i<=2; i++) { /* replace all bitmaps */
\r
2320 for (piece = WhitePawn;
\r
2321 (int) piece < (int) BlackPawn;
\r
2322 piece = (ChessSquare) ((int) piece + 1)) {
\r
2323 if (pieceBitmap[i][piece] != NULL)
\r
2324 DeleteObject(pieceBitmap[i][piece]);
\r
2327 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2328 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2329 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2330 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2331 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2332 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2333 pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2334 pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2335 pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2336 pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2337 pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2338 pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2339 pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2340 pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2341 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");
\r
2342 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");
\r
2343 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");
\r
2344 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");
\r
2345 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");
\r
2346 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");
\r
2347 pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");
\r
2348 pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");
\r
2349 pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");
\r
2350 pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");
\r
2351 pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");
\r
2352 pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");
\r
2353 pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");
\r
2354 pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");
\r
2355 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2356 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2357 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2358 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2359 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2360 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");
\r
2361 pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2362 pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2363 pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");
\r
2364 pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");
\r
2365 pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2366 pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");
\r
2367 pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");
\r
2368 pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");
\r
2374 PieceBitmap(ChessSquare p, int kind)
\r
2376 if ((int) p >= (int) BlackPawn)
\r
2377 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2379 return pieceBitmap[kind][(int) p];
\r
2382 /***************************************************************/
\r
2384 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2385 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2387 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2388 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2392 SquareToPos(int row, int column, int * x, int * y)
\r
2395 *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
2396 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2398 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2399 *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
2404 DrawCoordsOnDC(HDC hdc)
\r
2406 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
2407 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
2408 char str[2] = { NULLCHAR, NULLCHAR };
\r
2409 int oldMode, oldAlign, x, y, start, i;
\r
2413 if (!appData.showCoords)
\r
2416 start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;
\r
2418 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2419 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2420 oldAlign = GetTextAlign(hdc);
\r
2421 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2423 y = boardRect.top + lineGap;
\r
2424 x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);
\r
2426 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2427 for (i = 0; i < BOARD_HEIGHT; i++) {
\r
2428 str[0] = files[start + i];
\r
2429 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2430 y += squareSize + lineGap;
\r
2433 start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;
\r
2435 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2436 for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {
\r
2437 str[0] = ranks[start + i];
\r
2438 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2439 x += squareSize + lineGap;
\r
2442 SelectObject(hdc, oldBrush);
\r
2443 SetBkMode(hdc, oldMode);
\r
2444 SetTextAlign(hdc, oldAlign);
\r
2445 SelectObject(hdc, oldFont);
\r
2449 DrawGridOnDC(HDC hdc)
\r
2453 if (lineGap != 0) {
\r
2454 oldPen = SelectObject(hdc, gridPen);
\r
2455 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);
\r
2456 SelectObject(hdc, oldPen);
\r
2460 #define HIGHLIGHT_PEN 0
\r
2461 #define PREMOVE_PEN 1
\r
2464 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2467 HPEN oldPen, hPen;
\r
2468 if (lineGap == 0) return;
\r
2470 x1 = boardRect.left +
\r
2471 lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);
\r
2472 y1 = boardRect.top +
\r
2473 lineGap/2 + y * (squareSize + lineGap);
\r
2475 x1 = boardRect.left +
\r
2476 lineGap/2 + x * (squareSize + lineGap);
\r
2477 y1 = boardRect.top +
\r
2478 lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);
\r
2480 hPen = pen ? premovePen : highlightPen;
\r
2481 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2482 MoveToEx(hdc, x1, y1, NULL);
\r
2483 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2484 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2485 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2486 LineTo(hdc, x1, y1);
\r
2487 SelectObject(hdc, oldPen);
\r
2491 DrawHighlightsOnDC(HDC hdc)
\r
2494 for (i=0; i<2; i++) {
\r
2495 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2496 DrawHighlightOnDC(hdc, TRUE,
\r
2497 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2500 for (i=0; i<2; i++) {
\r
2501 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2502 premoveHighlightInfo.sq[i].y >= 0) {
\r
2503 DrawHighlightOnDC(hdc, TRUE,
\r
2504 premoveHighlightInfo.sq[i].x,
\r
2505 premoveHighlightInfo.sq[i].y,
\r
2511 /* Note: sqcolor is used only in monoMode */
\r
2512 /* Note that this code is largely duplicated in woptions.c,
\r
2513 function DrawSampleSquare, so that needs to be updated too */
\r
2515 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2517 HBITMAP oldBitmap;
\r
2521 if (appData.blindfold) return;
\r
2523 /* [AS] Use font-based pieces if needed */
\r
2524 if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {
\r
2525 /* Create piece bitmaps, or do nothing if piece set is up to date */
\r
2526 CreatePiecesFromFont();
\r
2528 if( fontBitmapSquareSize == squareSize ) {
\r
2529 int index = TranslatePieceToFontPiece(piece);
\r
2531 SelectObject( tmphdc, hPieceMask[ index ] );
\r
2535 squareSize, squareSize,
\r
2540 SelectObject( tmphdc, hPieceFace[ index ] );
\r
2544 squareSize, squareSize,
\r
2553 if (appData.monoMode) {
\r
2554 SelectObject(tmphdc, PieceBitmap(piece,
\r
2555 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2556 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2557 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2559 tmpSize = squareSize;
\r
2561 ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||
\r
2562 (piece >= (int)BlackNightrider && piece <= BlackGrasshopper)) ) {
\r
2563 /* [HGM] no bitmap available for promoted pieces in Crazyhouse */
\r
2564 /* Bitmaps of smaller size are substituted, but we have to align them */
\r
2565 x += (squareSize - minorSize)>>1;
\r
2566 y += squareSize - minorSize - 2;
\r
2567 tmpSize = minorSize;
\r
2569 if (color || appData.allWhite ) {
\r
2570 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2572 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2573 else oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2574 if(appData.upsideDown && color==flipView)
\r
2575 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2577 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2578 /* Use black for outline of white pieces */
\r
2579 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2580 if(appData.upsideDown && color==flipView)
\r
2581 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);
\r
2583 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);
\r
2585 /* Use square color for details of black pieces */
\r
2586 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2587 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2588 if(appData.upsideDown && !flipView)
\r
2589 StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);
\r
2591 BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);
\r
2593 SelectObject(hdc, oldBrush);
\r
2594 SelectObject(tmphdc, oldBitmap);
\r
2598 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */
\r
2599 int GetBackTextureMode( int algo )
\r
2601 int result = BACK_TEXTURE_MODE_DISABLED;
\r
2605 case BACK_TEXTURE_MODE_PLAIN:
\r
2606 result = 1; /* Always use identity map */
\r
2608 case BACK_TEXTURE_MODE_FULL_RANDOM:
\r
2609 result = 1 + (myrandom() % 3); /* Pick a transformation at random */
\r
2617 [AS] Compute and save texture drawing info, otherwise we may not be able
\r
2618 to handle redraws cleanly (as random numbers would always be different).
\r
2620 VOID RebuildTextureSquareInfo()
\r
2630 ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );
\r
2632 if( liteBackTexture != NULL ) {
\r
2633 if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2634 lite_w = bi.bmWidth;
\r
2635 lite_h = bi.bmHeight;
\r
2639 if( darkBackTexture != NULL ) {
\r
2640 if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {
\r
2641 dark_w = bi.bmWidth;
\r
2642 dark_h = bi.bmHeight;
\r
2646 for( row=0; row<BOARD_HEIGHT; row++ ) {
\r
2647 for( col=0; col<BOARD_WIDTH; col++ ) {
\r
2648 if( (col + row) & 1 ) {
\r
2650 if( lite_w >= squareSize && lite_h >= squareSize ) {
\r
2651 backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1); /* [HGM] divide by size-1 in stead of size! */
\r
2652 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);
\r
2653 backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);
\r
2658 if( dark_w >= squareSize && dark_h >= squareSize ) {
\r
2659 backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);
\r
2660 backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);
\r
2661 backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);
\r
2668 /* [AS] Arrow highlighting support */
\r
2670 static int A_WIDTH = 5; /* Width of arrow body */
\r
2672 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
\r
2673 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
\r
2675 static double Sqr( double x )
\r
2680 static int Round( double x )
\r
2682 return (int) (x + 0.5);
\r
2685 /* Draw an arrow between two points using current settings */
\r
2686 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )
\r
2689 double dx, dy, j, k, x, y;
\r
2691 if( d_x == s_x ) {
\r
2692 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2694 arrow[0].x = s_x + A_WIDTH;
\r
2697 arrow[1].x = s_x + A_WIDTH;
\r
2698 arrow[1].y = d_y - h;
\r
2700 arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;
\r
2701 arrow[2].y = d_y - h;
\r
2706 arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;
\r
2707 arrow[4].y = d_y - h;
\r
2709 arrow[5].x = s_x - A_WIDTH;
\r
2710 arrow[5].y = d_y - h;
\r
2712 arrow[6].x = s_x - A_WIDTH;
\r
2715 else if( d_y == s_y ) {
\r
2716 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
\r
2719 arrow[0].y = s_y + A_WIDTH;
\r
2721 arrow[1].x = d_x - w;
\r
2722 arrow[1].y = s_y + A_WIDTH;
\r
2724 arrow[2].x = d_x - w;
\r
2725 arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;
\r
2730 arrow[4].x = d_x - w;
\r
2731 arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;
\r
2733 arrow[5].x = d_x - w;
\r
2734 arrow[5].y = s_y - A_WIDTH;
\r
2737 arrow[6].y = s_y - A_WIDTH;
\r
2740 /* [AS] Needed a lot of paper for this! :-) */
\r
2741 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
\r
2742 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
\r
2744 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
\r
2746 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
\r
2751 arrow[0].x = Round(x - j);
\r
2752 arrow[0].y = Round(y + j*dx);
\r
2754 arrow[1].x = Round(x + j);
\r
2755 arrow[1].y = Round(y - j*dx);
\r
2758 x = (double) d_x - k;
\r
2759 y = (double) d_y - k*dy;
\r
2762 x = (double) d_x + k;
\r
2763 y = (double) d_y + k*dy;
\r
2766 arrow[2].x = Round(x + j);
\r
2767 arrow[2].y = Round(y - j*dx);
\r
2769 arrow[3].x = Round(x + j*A_WIDTH_FACTOR);
\r
2770 arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);
\r
2775 arrow[5].x = Round(x - j*A_WIDTH_FACTOR);
\r
2776 arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);
\r
2778 arrow[6].x = Round(x - j);
\r
2779 arrow[6].y = Round(y + j*dx);
\r
2782 Polygon( hdc, arrow, 7 );
\r
2785 /* [AS] Draw an arrow between two squares */
\r
2786 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )
\r
2788 int s_x, s_y, d_x, d_y;
\r
2795 if( s_col == d_col && s_row == d_row ) {
\r
2799 /* Get source and destination points */
\r
2800 SquareToPos( s_row, s_col, &s_x, &s_y);
\r
2801 SquareToPos( d_row, d_col, &d_x, &d_y);
\r
2804 d_y += squareSize / 4;
\r
2806 else if( d_y < s_y ) {
\r
2807 d_y += 3 * squareSize / 4;
\r
2810 d_y += squareSize / 2;
\r
2814 d_x += squareSize / 4;
\r
2816 else if( d_x < s_x ) {
\r
2817 d_x += 3 * squareSize / 4;
\r
2820 d_x += squareSize / 2;
\r
2823 s_x += squareSize / 2;
\r
2824 s_y += squareSize / 2;
\r
2826 /* Adjust width */
\r
2827 A_WIDTH = squareSize / 14;
\r
2830 stLB.lbStyle = BS_SOLID;
\r
2831 stLB.lbColor = appData.highlightArrowColor;
\r
2834 hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );
\r
2835 holdpen = SelectObject( hdc, hpen );
\r
2836 hbrush = CreateBrushIndirect( &stLB );
\r
2837 holdbrush = SelectObject( hdc, hbrush );
\r
2839 DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );
\r
2841 SelectObject( hdc, holdpen );
\r
2842 SelectObject( hdc, holdbrush );
\r
2843 DeleteObject( hpen );
\r
2844 DeleteObject( hbrush );
\r
2847 BOOL HasHighlightInfo()
\r
2849 BOOL result = FALSE;
\r
2851 if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&
\r
2852 highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )
\r
2860 BOOL IsDrawArrowEnabled()
\r
2862 BOOL result = FALSE;
\r
2864 if( appData.highlightMoveWithArrow && squareSize >= 32 ) {
\r
2871 VOID DrawArrowHighlight( HDC hdc )
\r
2873 if( IsDrawArrowEnabled() && HasHighlightInfo() ) {
\r
2874 DrawArrowBetweenSquares( hdc,
\r
2875 highlightInfo.sq[0].x, highlightInfo.sq[0].y,
\r
2876 highlightInfo.sq[1].x, highlightInfo.sq[1].y );
\r
2880 HRGN GetArrowHighlightClipRegion( HDC hdc )
\r
2882 HRGN result = NULL;
\r
2884 if( HasHighlightInfo() ) {
\r
2885 int x1, y1, x2, y2;
\r
2886 int sx, sy, dx, dy;
\r
2888 SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );
\r
2889 SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );
\r
2891 sx = MIN( x1, x2 );
\r
2892 sy = MIN( y1, y2 );
\r
2893 dx = MAX( x1, x2 ) + squareSize;
\r
2894 dy = MAX( y1, y2 ) + squareSize;
\r
2896 result = CreateRectRgn( sx, sy, dx, dy );
\r
2903 Warning: this function modifies the behavior of several other functions.
\r
2905 Basically, Winboard is optimized to avoid drawing the whole board if not strictly
\r
2906 needed. Unfortunately, the decision whether or not to perform a full or partial
\r
2907 repaint is scattered all over the place, which is not good for features such as
\r
2908 "arrow highlighting" that require a full repaint of the board.
\r
2910 So, I've tried to patch the code where I thought it made sense (e.g. after or during
\r
2911 user interaction, when speed is not so important) but especially to avoid errors
\r
2912 in the displayed graphics.
\r
2914 In such patched places, I always try refer to this function so there is a single
\r
2915 place to maintain knowledge.
\r
2917 To restore the original behavior, just return FALSE unconditionally.
\r
2919 BOOL IsFullRepaintPreferrable()
\r
2921 BOOL result = FALSE;
\r
2923 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {
\r
2924 /* Arrow may appear on the board */
\r
2932 This function is called by DrawPosition to know whether a full repaint must
\r
2935 Only DrawPosition may directly call this function, which makes use of
\r
2936 some state information. Other function should call DrawPosition specifying
\r
2937 the repaint flag, and can use IsFullRepaintPreferrable if needed.
\r
2939 BOOL DrawPositionNeedsFullRepaint()
\r
2941 BOOL result = FALSE;
\r
2944 Probably a slightly better policy would be to trigger a full repaint
\r
2945 when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),
\r
2946 but animation is fast enough that it's difficult to notice.
\r
2948 if( animInfo.piece == EmptySquare ) {
\r
2949 if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() /*&& HasHighlightInfo()*/ ) {
\r
2958 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2960 int row, column, x, y, square_color, piece_color;
\r
2961 ChessSquare piece;
\r
2963 HDC texture_hdc = NULL;
\r
2965 /* [AS] Initialize background textures if needed */
\r
2966 if( liteBackTexture != NULL || darkBackTexture != NULL ) {
\r
2967 static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */
\r
2968 if( backTextureSquareSize != squareSize
\r
2969 || backTextureBoardSize != BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT) {
\r
2970 backTextureBoardSize = BOARD_WIDTH+BOARD_FILES*BOARD_HEIGHT;
\r
2971 backTextureSquareSize = squareSize;
\r
2972 RebuildTextureSquareInfo();
\r
2975 texture_hdc = CreateCompatibleDC( hdc );
\r
2978 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
2979 for (column = 0; column < BOARD_WIDTH; column++) {
\r
2981 SquareToPos(row, column, &x, &y);
\r
2983 piece = board[row][column];
\r
2985 square_color = ((column + row) % 2) == 1;
\r
2986 if( gameInfo.variant == VariantXiangqi ) {
\r
2987 square_color = !InPalace(row, column);
\r
2988 if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }
\r
2989 else if(row < BOARD_HEIGHT/2) square_color ^= 1;
\r
2991 piece_color = (int) piece < (int) BlackPawn;
\r
2994 /* [HGM] holdings file: light square or black */
\r
2995 if(column == BOARD_LEFT-2) {
\r
2996 if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )
\r
2999 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */
\r
3003 if(column == BOARD_RGHT + 1 ) {
\r
3004 if( row < gameInfo.holdingsSize )
\r
3007 DisplayHoldingsCount(hdc, x, y, 0, 0);
\r
3011 if(column == BOARD_LEFT-1 ) /* left align */
\r
3012 DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);
\r
3013 else if( column == BOARD_RGHT) /* right align */
\r
3014 DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);
\r
3016 if (appData.monoMode) {
\r
3017 if (piece == EmptySquare) {
\r
3018 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
3019 square_color ? WHITENESS : BLACKNESS);
\r
3021 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
3024 else if( backTextureSquareInfo[row][column].mode > 0 ) {
\r
3025 /* [AS] Draw the square using a texture bitmap */
\r
3026 HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );
\r
3027 int r = row, c = column; // [HGM] do not flip board in flipView
\r
3028 if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }
\r
3031 squareSize, squareSize,
\r
3034 backTextureSquareInfo[r][c].mode,
\r
3035 backTextureSquareInfo[r][c].x,
\r
3036 backTextureSquareInfo[r][c].y );
\r
3038 SelectObject( texture_hdc, hbm );
\r
3040 if (piece != EmptySquare) {
\r
3041 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3045 HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;
\r
3047 oldBrush = SelectObject(hdc, brush );
\r
3048 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
3049 SelectObject(hdc, oldBrush);
\r
3050 if (piece != EmptySquare)
\r
3051 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
3056 if( texture_hdc != NULL ) {
\r
3057 DeleteDC( texture_hdc );
\r
3061 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag
\r
3062 void fputDW(FILE *f, int x)
\r
3064 fputc(x & 255, f);
\r
3065 fputc(x>>8 & 255, f);
\r
3066 fputc(x>>16 & 255, f);
\r
3067 fputc(x>>24 & 255, f);
\r
3070 #define MAX_CLIPS 200 /* more than enough */
\r
3073 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
\r
3075 // HBITMAP bufferBitmap;
\r
3080 int w = 100, h = 50;
\r
3082 if(logo == NULL) return;
\r
3083 // GetClientRect(hwndMain, &Rect);
\r
3084 // bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3085 // Rect.bottom-Rect.top+1);
\r
3086 tmphdc = CreateCompatibleDC(hdc);
\r
3087 hbm = SelectObject(tmphdc, logo);
\r
3088 if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {
\r
3092 StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left,
\r
3093 logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);
\r
3094 SelectObject(tmphdc, hbm);
\r
3098 static HDC hdcSeek;
\r
3100 // [HGM] seekgraph
\r
3101 void DrawSeekAxis( int x, int y, int xTo, int yTo )
\r
3104 HPEN hp = SelectObject( hdcSeek, gridPen );
\r
3105 MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );
\r
3106 LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );
\r
3107 SelectObject( hdcSeek, hp );
\r
3110 // front-end wrapper for drawing functions to do rectangles
\r
3111 void DrawSeekBackground( int left, int top, int right, int bottom )
\r
3116 if (hdcSeek == NULL) {
\r
3117 hdcSeek = GetDC(hwndMain);
\r
3118 if (!appData.monoMode) {
\r
3119 SelectPalette(hdcSeek, hPal, FALSE);
\r
3120 RealizePalette(hdcSeek);
\r
3123 hp = SelectObject( hdcSeek, gridPen );
\r
3124 rc.top = boardRect.top+top; rc.left = boardRect.left+left;
\r
3125 rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;
\r
3126 FillRect( hdcSeek, &rc, lightSquareBrush );
\r
3127 SelectObject( hdcSeek, hp );
\r
3130 // front-end wrapper for putting text in graph
\r
3131 void DrawSeekText(char *buf, int x, int y)
\r
3134 SetBkMode( hdcSeek, TRANSPARENT );
\r
3135 GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );
\r
3136 TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );
\r
3139 void DrawSeekDot(int x, int y, int color)
\r
3141 int square = color & 0x80;
\r
3143 HBRUSH oldBrush = SelectObject(hdcSeek,
\r
3144 color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);
\r
3146 Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,
\r
3147 boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);
\r
3149 Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,
\r
3150 boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);
\r
3151 SelectObject(hdcSeek, oldBrush);
\r
3155 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
3157 static Board lastReq, lastDrawn;
\r
3158 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
3159 static int lastDrawnFlipView = 0;
\r
3160 static int lastReqValid = 0, lastDrawnValid = 0;
\r
3161 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
3164 HBITMAP bufferBitmap;
\r
3165 HBITMAP oldBitmap;
\r
3167 HRGN clips[MAX_CLIPS];
\r
3168 ChessSquare dragged_piece = EmptySquare;
\r
3170 /* I'm undecided on this - this function figures out whether a full
\r
3171 * repaint is necessary on its own, so there's no real reason to have the
\r
3172 * caller tell it that. I think this can safely be set to FALSE - but
\r
3173 * if we trust the callers not to request full repaints unnessesarily, then
\r
3174 * we could skip some clipping work. In other words, only request a full
\r
3175 * redraw when the majority of pieces have changed positions (ie. flip,
\r
3176 * gamestart and similar) --Hawk
\r
3178 Boolean fullrepaint = repaint;
\r
3180 if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up
\r
3182 if( DrawPositionNeedsFullRepaint() ) {
\r
3183 fullrepaint = TRUE;
\r
3186 if (board == NULL) {
\r
3187 if (!lastReqValid) {
\r
3192 CopyBoard(lastReq, board);
\r
3196 if (doingSizing) {
\r
3200 if (IsIconic(hwndMain)) {
\r
3204 if (hdc == NULL) {
\r
3205 hdc = GetDC(hwndMain);
\r
3206 if (!appData.monoMode) {
\r
3207 SelectPalette(hdc, hPal, FALSE);
\r
3208 RealizePalette(hdc);
\r
3212 releaseDC = FALSE;
\r
3215 /* Create some work-DCs */
\r
3216 hdcmem = CreateCompatibleDC(hdc);
\r
3217 tmphdc = CreateCompatibleDC(hdc);
\r
3219 /* If dragging is in progress, we temporarely remove the piece */
\r
3220 /* [HGM] or temporarily decrease count if stacked */
\r
3221 /* !! Moved to before board compare !! */
\r
3222 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
3223 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
3224 if(dragInfo.from.x == BOARD_LEFT-2 ) {
\r
3225 if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )
\r
3226 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3228 if(dragInfo.from.x == BOARD_RGHT+1) {
\r
3229 if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )
\r
3230 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3232 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
3235 /* Figure out which squares need updating by comparing the
\r
3236 * newest board with the last drawn board and checking if
\r
3237 * flipping has changed.
\r
3239 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
3240 for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */
\r
3241 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3242 if (lastDrawn[row][column] != board[row][column]) {
\r
3243 SquareToPos(row, column, &x, &y);
\r
3244 clips[num_clips++] =
\r
3245 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
3249 for (i=0; i<2; i++) {
\r
3250 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
3251 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
3252 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
3253 lastDrawnHighlight.sq[i].y >= 0) {
\r
3254 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
3255 lastDrawnHighlight.sq[i].x, &x, &y);
\r
3256 clips[num_clips++] =
\r
3257 CreateRectRgn(x - lineGap, y - lineGap,
\r
3258 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3260 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
3261 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
3262 clips[num_clips++] =
\r
3263 CreateRectRgn(x - lineGap, y - lineGap,
\r
3264 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3268 for (i=0; i<2; i++) {
\r
3269 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
3270 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
3271 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
3272 lastDrawnPremove.sq[i].y >= 0) {
\r
3273 SquareToPos(lastDrawnPremove.sq[i].y,
\r
3274 lastDrawnPremove.sq[i].x, &x, &y);
\r
3275 clips[num_clips++] =
\r
3276 CreateRectRgn(x - lineGap, y - lineGap,
\r
3277 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3279 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
3280 premoveHighlightInfo.sq[i].y >= 0) {
\r
3281 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
3282 premoveHighlightInfo.sq[i].x, &x, &y);
\r
3283 clips[num_clips++] =
\r
3284 CreateRectRgn(x - lineGap, y - lineGap,
\r
3285 x + squareSize + lineGap, y + squareSize + lineGap);
\r
3290 fullrepaint = TRUE;
\r
3293 /* Create a buffer bitmap - this is the actual bitmap
\r
3294 * being written to. When all the work is done, we can
\r
3295 * copy it to the real DC (the screen). This avoids
\r
3296 * the problems with flickering.
\r
3298 GetClientRect(hwndMain, &Rect);
\r
3299 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
3300 Rect.bottom-Rect.top+1);
\r
3301 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
3302 if (!appData.monoMode) {
\r
3303 SelectPalette(hdcmem, hPal, FALSE);
\r
3306 /* Create clips for dragging */
\r
3307 if (!fullrepaint) {
\r
3308 if (dragInfo.from.x >= 0) {
\r
3309 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
3310 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3312 if (dragInfo.start.x >= 0) {
\r
3313 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
3314 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3316 if (dragInfo.pos.x >= 0) {
\r
3317 x = dragInfo.pos.x - squareSize / 2;
\r
3318 y = dragInfo.pos.y - squareSize / 2;
\r
3319 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3321 if (dragInfo.lastpos.x >= 0) {
\r
3322 x = dragInfo.lastpos.x - squareSize / 2;
\r
3323 y = dragInfo.lastpos.y - squareSize / 2;
\r
3324 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
3328 /* Are we animating a move?
\r
3330 * - remove the piece from the board (temporarely)
\r
3331 * - calculate the clipping region
\r
3333 if (!fullrepaint) {
\r
3334 if (animInfo.piece != EmptySquare) {
\r
3335 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
3336 x = boardRect.left + animInfo.lastpos.x;
\r
3337 y = boardRect.top + animInfo.lastpos.y;
\r
3338 x2 = boardRect.left + animInfo.pos.x;
\r
3339 y2 = boardRect.top + animInfo.pos.y;
\r
3340 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
3341 /* Slight kludge. The real problem is that after AnimateMove is
\r
3342 done, the position on the screen does not match lastDrawn.
\r
3343 This currently causes trouble only on e.p. captures in
\r
3344 atomic, where the piece moves to an empty square and then
\r
3345 explodes. The old and new positions both had an empty square
\r
3346 at the destination, but animation has drawn a piece there and
\r
3347 we have to remember to erase it. [HGM] moved until after setting lastDrawn */
\r
3348 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
3352 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
3353 if (num_clips == 0)
\r
3354 fullrepaint = TRUE;
\r
3356 /* Set clipping on the memory DC */
\r
3357 if (!fullrepaint) {
\r
3358 SelectClipRgn(hdcmem, clips[0]);
\r
3359 for (x = 1; x < num_clips; x++) {
\r
3360 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
3361 abort(); // this should never ever happen!
\r
3365 /* Do all the drawing to the memory DC */
\r
3366 if(explodeInfo.radius) { // [HGM] atomic
\r
3368 int x, y, r=(explodeInfo.radius * squareSize)/100;
\r
3369 board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer
\r
3370 SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);
\r
3371 x += squareSize/2;
\r
3372 y += squareSize/2;
\r
3373 if(!fullrepaint) {
\r
3374 clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);
\r
3375 ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);
\r
3377 DrawGridOnDC(hdcmem);
\r
3378 DrawHighlightsOnDC(hdcmem);
\r
3379 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3380 oldBrush = SelectObject(hdcmem, explodeBrush);
\r
3381 Ellipse(hdcmem, x-r, y-r, x+r, y+r);
\r
3382 SelectObject(hdcmem, oldBrush);
\r
3384 DrawGridOnDC(hdcmem);
\r
3385 DrawHighlightsOnDC(hdcmem);
\r
3386 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
3388 for (row = 0; row < BOARD_HEIGHT; row++) {
\r
3389 for (column = 0; column < BOARD_WIDTH; column++) {
\r
3390 if (marker[row][column]) { // marker changes only occur with full repaint!
\r
3391 HBRUSH oldBrush = SelectObject(hdcmem,
\r
3392 marker[row][column] == 2 ? markerBrush : explodeBrush);
\r
3393 SquareToPos(row, column, &x, &y);
\r
3394 Ellipse(hdcmem, x + squareSize/4, y + squareSize/4,
\r
3395 x + 3*squareSize/4, y + 3*squareSize/4);
\r
3396 SelectObject(hdcmem, oldBrush);
\r
3401 HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;
\r
3402 if(appData.autoLogo) {
\r
3404 switch(gameMode) { // pick logos based on game mode
\r
3405 case IcsObserving:
\r
3406 whiteLogo = second.programLogo; // ICS logo
\r
3407 blackLogo = second.programLogo;
\r
3410 case IcsPlayingWhite:
\r
3411 if(!appData.zippyPlay) whiteLogo = userLogo;
\r
3412 blackLogo = second.programLogo; // ICS logo
\r
3414 case IcsPlayingBlack:
\r
3415 whiteLogo = second.programLogo; // ICS logo
\r
3416 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;
\r
3418 case TwoMachinesPlay:
\r
3419 if(first.twoMachinesColor[0] == 'b') {
\r
3420 whiteLogo = second.programLogo;
\r
3421 blackLogo = first.programLogo;
\r
3424 case MachinePlaysWhite:
\r
3425 blackLogo = userLogo;
\r
3427 case MachinePlaysBlack:
\r
3428 whiteLogo = userLogo;
\r
3429 blackLogo = first.programLogo;
\r
3432 DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);
\r
3433 DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);
\r
3436 if( appData.highlightMoveWithArrow ) {
\r
3437 DrawArrowHighlight(hdcmem);
\r
3440 DrawCoordsOnDC(hdcmem);
\r
3442 CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */
\r
3443 /* to make sure lastDrawn contains what is actually drawn */
\r
3445 /* Put the dragged piece back into place and draw it (out of place!) */
\r
3446 if (dragged_piece != EmptySquare) {
\r
3447 /* [HGM] or restack */
\r
3448 if(dragInfo.from.x == BOARD_LEFT-2 )
\r
3449 board[dragInfo.from.y][dragInfo.from.x+1]++;
\r
3451 if(dragInfo.from.x == BOARD_RGHT+1 )
\r
3452 board[dragInfo.from.y][dragInfo.from.x-1]++;
\r
3453 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
3454 x = dragInfo.pos.x - squareSize / 2;
\r
3455 y = dragInfo.pos.y - squareSize / 2;
\r
3456 DrawPieceOnDC(hdcmem, dragged_piece,
\r
3457 ((int) dragged_piece < (int) BlackPawn),
\r
3458 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
3461 /* Put the animated piece back into place and draw it */
\r
3462 if (animInfo.piece != EmptySquare) {
\r
3463 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
3464 x = boardRect.left + animInfo.pos.x;
\r
3465 y = boardRect.top + animInfo.pos.y;
\r
3466 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
3467 ((int) animInfo.piece < (int) BlackPawn),
\r
3468 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
3471 /* Release the bufferBitmap by selecting in the old bitmap
\r
3472 * and delete the memory DC
\r
3474 SelectObject(hdcmem, oldBitmap);
\r
3477 /* Set clipping on the target DC */
\r
3478 if (!fullrepaint) {
\r
3479 SelectClipRgn(hdc, clips[0]);
\r
3480 for (x = 1; x < num_clips; x++) {
\r
3481 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
3482 abort(); // this should never ever happen!
\r
3486 /* Copy the new bitmap onto the screen in one go.
\r
3487 * This way we avoid any flickering
\r
3489 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
3490 BitBlt(hdc, boardRect.left, boardRect.top,
\r
3491 boardRect.right - boardRect.left,
\r
3492 boardRect.bottom - boardRect.top,
\r
3493 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
3494 if(saveDiagFlag) {
\r
3495 BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000];
\r
3496 BITMAPINFOHEADER bih; int color[16], nrColors=0;
\r
3498 GetObject(bufferBitmap, sizeof(b), &b);
\r
3499 if(b.bmWidthBytes*b.bmHeight <= 990000) {
\r
3500 bih.biSize = sizeof(BITMAPINFOHEADER);
\r
3501 bih.biWidth = b.bmWidth;
\r
3502 bih.biHeight = b.bmHeight;
\r
3504 bih.biBitCount = b.bmBitsPixel;
\r
3505 bih.biCompression = 0;
\r
3506 bih.biSizeImage = b.bmWidthBytes*b.bmHeight;
\r
3507 bih.biXPelsPerMeter = 0;
\r
3508 bih.biYPelsPerMeter = 0;
\r
3509 bih.biClrUsed = 0;
\r
3510 bih.biClrImportant = 0;
\r
3511 // fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n",
\r
3512 // b.bmType, b.bmWidth, b.bmHeight, b.bmWidthBytes, b.bmPlanes, b.bmBitsPixel);
\r
3513 GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);
\r
3514 // fprintf(diagFile, "%8x\n", (int) pData);
\r
3516 wb = b.bmWidthBytes;
\r
3518 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {
\r
3519 int k = ((int*) pData)[i];
\r
3520 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3521 if(j >= 16) break;
\r
3523 if(j >= nrColors) nrColors = j+1;
\r
3525 if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel
\r
3527 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {
\r
3528 for(w=0; w<(wb>>2); w+=2) {
\r
3529 int k = ((int*) pData)[(wb*i>>2) + w];
\r
3530 for(j=0; j<nrColors; j++) if(color[j] == k) break;
\r
3531 k = ((int*) pData)[(wb*i>>2) + w + 1];
\r
3532 for(m=0; m<nrColors; m++) if(color[m] == k) break;
\r
3533 pData[p++] = m | j<<4;
\r
3535 while(p&3) pData[p++] = 0;
\r
3538 wb = ((wb+31)>>5)<<2;
\r
3540 // write BITMAPFILEHEADER
\r
3541 fprintf(diagFile, "BM");
\r
3542 fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));
\r
3543 fputDW(diagFile, 0);
\r
3544 fputDW(diagFile, 0x36 + (fac?64:0));
\r
3545 // write BITMAPINFOHEADER
\r
3546 fputDW(diagFile, 40);
\r
3547 fputDW(diagFile, b.bmWidth);
\r
3548 fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);
\r
3549 if(fac) fputDW(diagFile, 0x040001); // planes and bits/pixel
\r
3550 else fputDW(diagFile, 0x200001); // planes and bits/pixel
\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 fputDW(diagFile, 0);
\r
3557 // write color table
\r
3559 for(i=0; i<16; i++) fputDW(diagFile, color[i]);
\r
3560 // write bitmap data
\r
3561 for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++)
\r
3562 fputc(pData[i], diagFile);
\r
3566 SelectObject(tmphdc, oldBitmap);
\r
3568 /* Massive cleanup */
\r
3569 for (x = 0; x < num_clips; x++)
\r
3570 DeleteObject(clips[x]);
\r
3573 DeleteObject(bufferBitmap);
\r
3576 ReleaseDC(hwndMain, hdc);
\r
3578 if (lastDrawnFlipView != flipView) {
\r
3580 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
3582 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
3585 /* CopyBoard(lastDrawn, board);*/
\r
3586 lastDrawnHighlight = highlightInfo;
\r
3587 lastDrawnPremove = premoveHighlightInfo;
\r
3588 lastDrawnFlipView = flipView;
\r
3589 lastDrawnValid = 1;
\r
3592 /* [HGM] diag: Save the current board display to the given open file and close the file */
\r
3597 saveDiagFlag = 1; diagFile = f;
\r
3598 HDCDrawPosition(NULL, TRUE, NULL);
\r
3602 // if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");
\r
3609 /*---------------------------------------------------------------------------*\
\r
3610 | CLIENT PAINT PROCEDURE
\r
3611 | This is the main event-handler for the WM_PAINT message.
\r
3613 \*---------------------------------------------------------------------------*/
\r
3615 PaintProc(HWND hwnd)
\r
3621 if((hdc = BeginPaint(hwnd, &ps))) {
\r
3622 if (IsIconic(hwnd)) {
\r
3623 DrawIcon(hdc, 2, 2, iconCurrent);
\r
3625 if (!appData.monoMode) {
\r
3626 SelectPalette(hdc, hPal, FALSE);
\r
3627 RealizePalette(hdc);
\r
3629 HDCDrawPosition(hdc, 1, NULL);
\r
3631 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
3632 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
3633 ETO_CLIPPED|ETO_OPAQUE,
\r
3634 &messageRect, messageText, strlen(messageText), NULL);
\r
3635 SelectObject(hdc, oldFont);
\r
3636 DisplayBothClocks();
\r
3638 EndPaint(hwnd,&ps);
\r
3646 * If the user selects on a border boundary, return -1; if off the board,
\r
3647 * return -2. Otherwise map the event coordinate to the square.
\r
3648 * The offset boardRect.left or boardRect.top must already have been
\r
3649 * subtracted from x.
\r
3651 int EventToSquare(x, limit)
\r
3659 if ((x % (squareSize + lineGap)) >= squareSize)
\r
3661 x /= (squareSize + lineGap);
\r
3673 DropEnable dropEnables[] = {
\r
3674 { 'P', DP_Pawn, "Pawn" },
\r
3675 { 'N', DP_Knight, "Knight" },
\r
3676 { 'B', DP_Bishop, "Bishop" },
\r
3677 { 'R', DP_Rook, "Rook" },
\r
3678 { 'Q', DP_Queen, "Queen" },
\r
3682 SetupDropMenu(HMENU hmenu)
\r
3684 int i, count, enable;
\r
3686 extern char white_holding[], black_holding[];
\r
3687 char item[MSG_SIZ];
\r
3689 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
3690 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
3691 dropEnables[i].piece);
\r
3693 while (p && *p++ == dropEnables[i].piece) count++;
\r
3694 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
3695 enable = count > 0 || !appData.testLegality
\r
3696 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
3697 && !appData.icsActive);
\r
3698 ModifyMenu(hmenu, dropEnables[i].command,
\r
3699 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
3700 dropEnables[i].command, item);
\r
3704 void DragPieceBegin(int x, int y)
\r
3706 dragInfo.lastpos.x = boardRect.left + x;
\r
3707 dragInfo.lastpos.y = boardRect.top + y;
\r
3708 dragInfo.from.x = fromX;
\r
3709 dragInfo.from.y = fromY;
\r
3710 dragInfo.start = dragInfo.from;
\r
3711 SetCapture(hwndMain);
\r
3714 void DragPieceEnd(int x, int y)
\r
3717 dragInfo.start.x = dragInfo.start.y = -1;
\r
3718 dragInfo.from = dragInfo.start;
\r
3719 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3722 /* Event handler for mouse messages */
\r
3724 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3728 static int recursive = 0;
\r
3730 BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */
\r
3733 if (message == WM_MBUTTONUP) {
\r
3734 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
3735 to the middle button: we simulate pressing the left button too!
\r
3737 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
3738 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
3744 pt.x = LOWORD(lParam);
\r
3745 pt.y = HIWORD(lParam);
\r
3746 x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);
\r
3747 y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);
\r
3748 if (!flipView && y >= 0) {
\r
3749 y = BOARD_HEIGHT - 1 - y;
\r
3751 if (flipView && x >= 0) {
\r
3752 x = BOARD_WIDTH - 1 - x;
\r
3755 switch (message) {
\r
3756 case WM_LBUTTONDOWN:
\r
3757 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3758 if (gameMode == EditPosition) {
\r
3759 SetWhiteToPlayEvent();
\r
3760 } else if (gameMode == IcsPlayingBlack ||
\r
3761 gameMode == MachinePlaysWhite) {
\r
3763 } else if (gameMode == EditGame) {
\r
3764 AdjustClock(flipClock, -1);
\r
3766 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3767 if (gameMode == EditPosition) {
\r
3768 SetBlackToPlayEvent();
\r
3769 } else if (gameMode == IcsPlayingWhite ||
\r
3770 gameMode == MachinePlaysBlack) {
\r
3772 } else if (gameMode == EditGame) {
\r
3773 AdjustClock(!flipClock, -1);
\r
3776 dragInfo.start.x = dragInfo.start.y = -1;
\r
3777 dragInfo.from = dragInfo.start;
\r
3778 if(fromX == -1 && frozen) { // not sure where this is for
\r
3779 fromX = fromY = -1;
\r
3780 DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */
\r
3783 LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3784 DrawPosition(TRUE, NULL);
\r
3787 case WM_LBUTTONUP:
\r
3788 LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);
\r
3789 DrawPosition(TRUE, NULL);
\r
3792 case WM_MOUSEMOVE:
\r
3793 if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;
\r
3794 MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
\r
3795 if ((appData.animateDragging || appData.highlightDragging)
\r
3796 && (wParam & MK_LBUTTON)
\r
3797 && dragInfo.from.x >= 0)
\r
3799 BOOL full_repaint = FALSE;
\r
3801 if (appData.animateDragging) {
\r
3802 dragInfo.pos = pt;
\r
3804 if (appData.highlightDragging) {
\r
3805 SetHighlights(fromX, fromY, x, y);
\r
3806 if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {
\r
3807 full_repaint = TRUE;
\r
3811 DrawPosition( full_repaint, NULL);
\r
3813 dragInfo.lastpos = dragInfo.pos;
\r
3817 case WM_MOUSEWHEEL: // [DM]
\r
3818 { static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events
\r
3819 /* Mouse Wheel is being rolled forward
\r
3820 * Play moves forward
\r
3822 if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove)
\r
3823 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction
\r
3824 /* Mouse Wheel is being rolled backward
\r
3825 * Play moves backward
\r
3827 if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove)
\r
3828 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }
\r
3832 case WM_MBUTTONUP:
\r
3833 case WM_RBUTTONUP:
\r
3835 RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3838 case WM_MBUTTONDOWN:
\r
3839 case WM_RBUTTONDOWN:
\r
3842 fromX = fromY = -1;
\r
3843 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3844 dragInfo.start.x = dragInfo.start.y = -1;
\r
3845 dragInfo.from = dragInfo.start;
\r
3846 dragInfo.lastpos = dragInfo.pos;
\r
3847 if (appData.highlightDragging) {
\r
3848 ClearHighlights();
\r
3851 /* [HGM] right mouse button in clock area edit-game mode ups clock */
\r
3852 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
3853 if (gameMode == EditGame) AdjustClock(flipClock, 1);
\r
3854 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
3855 if (gameMode == EditGame) AdjustClock(!flipClock, 1);
\r
3858 DrawPosition(TRUE, NULL);
\r
3860 menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);
\r
3863 if (message == WM_MBUTTONDOWN) {
\r
3864 buttonCount = 3; /* even if system didn't think so */
\r
3865 if (wParam & MK_SHIFT)
\r
3866 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3868 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3869 } else { /* message == WM_RBUTTONDOWN */
\r
3870 /* Just have one menu, on the right button. Windows users don't
\r
3871 think to try the middle one, and sometimes other software steals
\r
3872 it, or it doesn't really exist. */
\r
3873 if(gameInfo.variant != VariantShogi)
\r
3874 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3876 MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);
\r
3880 SetCapture(hwndMain);
3883 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3884 SetupDropMenu(hmenu);
\r
3885 MenuPopup(hwnd, pt, hmenu, -1);
\r
3895 /* Preprocess messages for buttons in main window */
\r
3897 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3899 int id = GetWindowLong(hwnd, GWL_ID);
\r
3902 for (i=0; i<N_BUTTONS; i++) {
\r
3903 if (buttonDesc[i].id == id) break;
\r
3905 if (i == N_BUTTONS) return 0;
\r
3906 switch (message) {
\r
3911 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3912 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3919 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3922 if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {
\r
3923 // [HGM] movenum: only letters or leading zero should go to ICS input
\r
3924 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3925 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3927 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3929 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3930 PopUpMoveDialog((char)wParam);
\r
3936 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3939 /* Process messages for Promotion dialog box */
\r
3941 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3945 switch (message) {
\r
3946 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3947 /* Center the dialog over the application window */
\r
3948 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3949 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3950 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3951 gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?
\r
3952 SW_SHOW : SW_HIDE);
\r
3953 /* [HGM] Only allow C & A promotions if these pieces are defined */
\r
3954 ShowWindow(GetDlgItem(hDlg, PB_Archbishop),
\r
3955 ((PieceToChar(WhiteAngel) >= 'A' &&
\r
3956 PieceToChar(WhiteAngel) != '~') ||
\r
3957 (PieceToChar(BlackAngel) >= 'A' &&
\r
3958 PieceToChar(BlackAngel) != '~') ) ?
\r
3959 SW_SHOW : SW_HIDE);
\r
3960 ShowWindow(GetDlgItem(hDlg, PB_Chancellor),
\r
3961 ((PieceToChar(WhiteMarshall) >= 'A' &&
\r
3962 PieceToChar(WhiteMarshall) != '~') ||
\r
3963 (PieceToChar(BlackMarshall) >= 'A' &&
\r
3964 PieceToChar(BlackMarshall) != '~') ) ?
\r
3965 SW_SHOW : SW_HIDE);
\r
3966 /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */
\r
3967 ShowWindow(GetDlgItem(hDlg, PB_Rook),
\r
3968 gameInfo.variant != VariantShogi ?
\r
3969 SW_SHOW : SW_HIDE);
\r
3970 ShowWindow(GetDlgItem(hDlg, PB_Bishop),
\r
3971 gameInfo.variant != VariantShogi ?
\r
3972 SW_SHOW : SW_HIDE);
\r
3973 ShowWindow(GetDlgItem(hDlg, IDC_Yes),
\r
3974 gameInfo.variant == VariantShogi ?
\r
3975 SW_SHOW : SW_HIDE);
\r
3976 ShowWindow(GetDlgItem(hDlg, IDC_No),
\r
3977 gameInfo.variant == VariantShogi ?
\r
3978 SW_SHOW : SW_HIDE);
\r
3979 ShowWindow(GetDlgItem(hDlg, IDC_Centaur),
\r
3980 gameInfo.variant == VariantSuper ?
\r
3981 SW_SHOW : SW_HIDE);
\r
3984 case WM_COMMAND: /* message: received a command */
\r
3985 switch (LOWORD(wParam)) {
\r
3987 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3988 ClearHighlights();
\r
3989 DrawPosition(FALSE, NULL);
\r
3992 promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);
\r
3995 promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);
\r
3998 promoChar = PieceToChar(BlackRook);
\r
4001 promoChar = PieceToChar(BlackBishop);
\r
4003 case PB_Chancellor:
\r
4004 promoChar = PieceToChar(BlackMarshall);
\r
4006 case PB_Archbishop:
\r
4007 promoChar = PieceToChar(BlackAngel);
\r
4010 promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);
\r
4015 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
4016 /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we
\r
4017 only show the popup when we are already sure the move is valid or
\r
4018 legal. We pass a faulty move type, but the kludge is that FinishMove
\r
4019 will figure out it is a promotion from the promoChar. */
\r
4020 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
4021 fromX = fromY = -1;
\r
4022 if (!appData.highlightLastMove) {
\r
4023 ClearHighlights();
\r
4024 DrawPosition(FALSE, NULL);
\r
4031 /* Pop up promotion dialog */
\r
4033 PromotionPopup(HWND hwnd)
\r
4037 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
4038 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
4039 hwnd, (DLGPROC)lpProc);
\r
4040 FreeProcInstance(lpProc);
\r
4046 DrawPosition(TRUE, NULL);
\r
4047 PromotionPopup(hwndMain);
\r
4050 /* Toggle ShowThinking */
\r
4052 ToggleShowThinking()
\r
4054 appData.showThinking = !appData.showThinking;
\r
4055 ShowThinkingEvent();
\r
4059 LoadGameDialog(HWND hwnd, char* title)
\r
4063 char fileTitle[MSG_SIZ];
\r
4064 f = OpenFileDialog(hwnd, "rb", "",
\r
4065 appData.oldSaveStyle ? "gam" : "pgn",
\r
4067 title, &number, fileTitle, NULL);
\r
4069 cmailMsgLoaded = FALSE;
\r
4070 if (number == 0) {
\r
4071 int error = GameListBuild(f);
\r
4073 DisplayError("Cannot build game list", error);
\r
4074 } else if (!ListEmpty(&gameList) &&
\r
4075 ((ListGame *) gameList.tailPred)->number > 1) {
\r
4076 GameListPopUp(f, fileTitle);
\r
4079 GameListDestroy();
\r
4082 LoadGame(f, number, fileTitle, FALSE);
\r
4086 int get_term_width()
\r
4091 HFONT hfont, hold_font;
\r
4096 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4100 // get the text metrics
\r
4101 hdc = GetDC(hText);
\r
4102 lf = font[boardSize][CONSOLE_FONT]->lf;
\r
4103 if (consoleCF.dwEffects & CFE_BOLD)
\r
4104 lf.lfWeight = FW_BOLD;
\r
4105 if (consoleCF.dwEffects & CFE_ITALIC)
\r
4106 lf.lfItalic = TRUE;
\r
4107 if (consoleCF.dwEffects & CFE_STRIKEOUT)
\r
4108 lf.lfStrikeOut = TRUE;
\r
4109 if (consoleCF.dwEffects & CFE_UNDERLINE)
\r
4110 lf.lfUnderline = TRUE;
\r
4111 hfont = CreateFontIndirect(&lf);
\r
4112 hold_font = SelectObject(hdc, hfont);
\r
4113 GetTextMetrics(hdc, &tm);
\r
4114 SelectObject(hdc, hold_font);
\r
4115 DeleteObject(hfont);
\r
4116 ReleaseDC(hText, hdc);
\r
4118 // get the rectangle
\r
4119 SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);
\r
4121 return (rc.right-rc.left) / tm.tmAveCharWidth;
\r
4124 void UpdateICSWidth(HWND hText)
\r
4126 LONG old_width, new_width;
\r
4128 new_width = get_term_width(hText, FALSE);
\r
4129 old_width = GetWindowLong(hText, GWL_USERDATA);
\r
4130 if (new_width != old_width)
\r
4132 ics_update_width(new_width);
\r
4133 SetWindowLong(hText, GWL_USERDATA, new_width);
\r
4138 ChangedConsoleFont()
\r
4141 CHARRANGE tmpsel, sel;
\r
4142 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
4143 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
4144 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4147 cfmt.cbSize = sizeof(CHARFORMAT);
\r
4148 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
4149 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
4150 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
4151 * size. This was undocumented in the version of MSVC++ that I had
\r
4152 * when I wrote the code, but is apparently documented now.
\r
4154 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
4155 cfmt.bCharSet = f->lf.lfCharSet;
\r
4156 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
4157 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4158 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
4159 /* Why are the following seemingly needed too? */
\r
4160 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4161 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
4162 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
4164 tmpsel.cpMax = -1; /*999999?*/
\r
4165 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
4166 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
4167 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
4168 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
4170 paraf.cbSize = sizeof(paraf);
\r
4171 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
4172 paraf.dxStartIndent = 0;
\r
4173 paraf.dxOffset = WRAP_INDENT;
\r
4174 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
4175 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
4176 UpdateICSWidth(hText);
\r
4179 /*---------------------------------------------------------------------------*\
\r
4181 * Window Proc for main window
\r
4183 \*---------------------------------------------------------------------------*/
\r
4185 /* Process messages for main window, etc. */
\r
4187 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
4190 int wmId, wmEvent;
\r
4194 char fileTitle[MSG_SIZ];
\r
4195 char buf[MSG_SIZ];
\r
4196 static SnapData sd;
\r
4198 switch (message) {
\r
4200 case WM_PAINT: /* message: repaint portion of window */
\r
4204 case WM_ERASEBKGND:
\r
4205 if (IsIconic(hwnd)) {
\r
4206 /* Cheat; change the message */
\r
4207 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
4209 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
4213 case WM_LBUTTONDOWN:
\r
4214 case WM_MBUTTONDOWN:
\r
4215 case WM_RBUTTONDOWN:
\r
4216 case WM_LBUTTONUP:
\r
4217 case WM_MBUTTONUP:
\r
4218 case WM_RBUTTONUP:
\r
4219 case WM_MOUSEMOVE:
\r
4220 case WM_MOUSEWHEEL:
\r
4221 MouseEvent(hwnd, message, wParam, lParam);
\r
4224 JAWS_KB_NAVIGATION
\r
4228 JAWS_ALT_INTERCEPT
\r
4230 if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) {
\r
4231 // [HGM] movenum: for non-zero digits we always do type-in dialog
\r
4232 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
4233 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
4235 SendMessage(h, message, wParam, lParam);
\r
4236 } else if(lParam != KF_REPEAT) {
\r
4237 if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
4238 PopUpMoveDialog((char)wParam);
\r
4239 } else if((char)wParam == 003) CopyGameToClipboard();
\r
4240 else if((char)wParam == 026) PasteGameOrFENFromClipboard();
\r
4245 case WM_PALETTECHANGED:
\r
4246 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
4248 HDC hdc = GetDC(hwndMain);
\r
4249 SelectPalette(hdc, hPal, TRUE);
\r
4250 nnew = RealizePalette(hdc);
\r
4252 paletteChanged = TRUE;
\r
4253 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4255 ReleaseDC(hwnd, hdc);
\r
4259 case WM_QUERYNEWPALETTE:
\r
4260 if (!appData.monoMode /*&& paletteChanged*/) {
\r
4262 HDC hdc = GetDC(hwndMain);
\r
4263 paletteChanged = FALSE;
\r
4264 SelectPalette(hdc, hPal, FALSE);
\r
4265 nnew = RealizePalette(hdc);
\r
4267 InvalidateRect(hwnd, &boardRect, FALSE);
\r
4269 ReleaseDC(hwnd, hdc);
\r
4274 case WM_COMMAND: /* message: command from application menu */
\r
4275 wmId = LOWORD(wParam);
\r
4276 wmEvent = HIWORD(wParam);
\r
4281 SAY("new game enter a move to play against the computer with white");
\r
4284 case IDM_NewGameFRC:
\r
4285 if( NewGameFRC() == 0 ) {
\r
4290 case IDM_NewVariant:
\r
4291 NewVariantPopup(hwnd);
\r
4294 case IDM_LoadGame:
\r
4295 LoadGameDialog(hwnd, "Load Game from File");
\r
4298 case IDM_LoadNextGame:
\r
4302 case IDM_LoadPrevGame:
\r
4306 case IDM_ReloadGame:
\r
4310 case IDM_LoadPosition:
\r
4311 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
4312 Reset(FALSE, TRUE);
\r
4315 f = OpenFileDialog(hwnd, "rb", "",
\r
4316 appData.oldSaveStyle ? "pos" : "fen",
\r
4318 "Load Position from File", &number, fileTitle, NULL);
\r
4320 LoadPosition(f, number, fileTitle);
\r
4324 case IDM_LoadNextPosition:
\r
4325 ReloadPosition(1);
\r
4328 case IDM_LoadPrevPosition:
\r
4329 ReloadPosition(-1);
\r
4332 case IDM_ReloadPosition:
\r
4333 ReloadPosition(0);
\r
4336 case IDM_SaveGame:
\r
4337 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
4338 f = OpenFileDialog(hwnd, "a", defName,
\r
4339 appData.oldSaveStyle ? "gam" : "pgn",
\r
4341 "Save Game to File", NULL, fileTitle, NULL);
\r
4343 SaveGame(f, 0, "");
\r
4347 case IDM_SavePosition:
\r
4348 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
4349 f = OpenFileDialog(hwnd, "a", defName,
\r
4350 appData.oldSaveStyle ? "pos" : "fen",
\r
4352 "Save Position to File", NULL, fileTitle, NULL);
\r
4354 SavePosition(f, 0, "");
\r
4358 case IDM_SaveDiagram:
\r
4359 defName = "diagram";
\r
4360 f = OpenFileDialog(hwnd, "wb", defName,
\r
4363 "Save Diagram to File", NULL, fileTitle, NULL);
\r
4369 case IDM_CopyGame:
\r
4370 CopyGameToClipboard();
\r
4373 case IDM_PasteGame:
\r
4374 PasteGameFromClipboard();
\r
4377 case IDM_CopyGameListToClipboard:
\r
4378 CopyGameListToClipboard();
\r
4381 /* [AS] Autodetect FEN or PGN data */
\r
4382 case IDM_PasteAny:
\r
4383 PasteGameOrFENFromClipboard();
\r
4386 /* [AS] Move history */
\r
4387 case IDM_ShowMoveHistory:
\r
4388 if( MoveHistoryIsUp() ) {
\r
4389 MoveHistoryPopDown();
\r
4392 MoveHistoryPopUp();
\r
4396 /* [AS] Eval graph */
\r
4397 case IDM_ShowEvalGraph:
\r
4398 if( EvalGraphIsUp() ) {
\r
4399 EvalGraphPopDown();
\r
4403 SetFocus(hwndMain);
\r
4407 /* [AS] Engine output */
\r
4408 case IDM_ShowEngineOutput:
\r
4409 if( EngineOutputIsUp() ) {
\r
4410 EngineOutputPopDown();
\r
4413 EngineOutputPopUp();
\r
4417 /* [AS] User adjudication */
\r
4418 case IDM_UserAdjudication_White:
\r
4419 UserAdjudicationEvent( +1 );
\r
4422 case IDM_UserAdjudication_Black:
\r
4423 UserAdjudicationEvent( -1 );
\r
4426 case IDM_UserAdjudication_Draw:
\r
4427 UserAdjudicationEvent( 0 );
\r
4430 /* [AS] Game list options dialog */
\r
4431 case IDM_GameListOptions:
\r
4432 GameListOptions();
\r
4439 case IDM_CopyPosition:
\r
4440 CopyFENToClipboard();
\r
4443 case IDM_PastePosition:
\r
4444 PasteFENFromClipboard();
\r
4447 case IDM_MailMove:
\r
4451 case IDM_ReloadCMailMsg:
\r
4452 Reset(TRUE, TRUE);
\r
4453 ReloadCmailMsgEvent(FALSE);
\r
4456 case IDM_Minimize:
\r
4457 ShowWindow(hwnd, SW_MINIMIZE);
\r
4464 case IDM_MachineWhite:
\r
4465 MachineWhiteEvent();
\r
4467 * refresh the tags dialog only if it's visible
\r
4469 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
4471 tags = PGNTags(&gameInfo);
\r
4472 TagsPopUp(tags, CmailMsg());
\r
4475 SAY("computer starts playing white");
\r
4478 case IDM_MachineBlack:
\r
4479 MachineBlackEvent();
\r
4481 * refresh the tags dialog only if it's visible
\r
4483 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
4485 tags = PGNTags(&gameInfo);
\r
4486 TagsPopUp(tags, CmailMsg());
\r
4489 SAY("computer starts playing black");
\r
4492 case IDM_TwoMachines:
\r
4493 TwoMachinesEvent();
\r
4495 * refresh the tags dialog only if it's visible
\r
4497 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
4499 tags = PGNTags(&gameInfo);
\r
4500 TagsPopUp(tags, CmailMsg());
\r
4503 SAY("programs start playing each other");
\r
4506 case IDM_AnalysisMode:
\r
4507 if (!first.analysisSupport) {
\r
4508 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4509 DisplayError(buf, 0);
\r
4511 SAY("analyzing current position");
\r
4512 /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */
\r
4513 if (appData.icsActive) {
\r
4514 if (gameMode != IcsObserving) {
\r
4515 sprintf(buf, "You are not observing a game");
\r
4516 DisplayError(buf, 0);
\r
4517 /* secure check */
\r
4518 if (appData.icsEngineAnalyze) {
\r
4519 if (appData.debugMode)
\r
4520 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
\r
4521 ExitAnalyzeMode();
\r
4527 /* if enable, user want disable icsEngineAnalyze */
\r
4528 if (appData.icsEngineAnalyze) {
\r
4529 ExitAnalyzeMode();
\r
4533 appData.icsEngineAnalyze = TRUE;
\r
4534 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
\r
4537 if (!appData.showThinking) ToggleShowThinking();
\r
4538 AnalyzeModeEvent();
\r
4542 case IDM_AnalyzeFile:
\r
4543 if (!first.analysisSupport) {
\r
4544 char buf[MSG_SIZ];
\r
4545 sprintf(buf, "%s does not support analysis", first.tidy);
\r
4546 DisplayError(buf, 0);
\r
4548 if (!appData.showThinking) ToggleShowThinking();
\r
4549 AnalyzeFileEvent();
\r
4550 LoadGameDialog(hwnd, "Analyze Game from File");
\r
4551 AnalysisPeriodicEvent(1);
\r
4555 case IDM_IcsClient:
\r
4559 case IDM_EditGame:
\r
4564 case IDM_EditPosition:
\r
4565 EditPositionEvent();
\r
4566 SAY("to set up a position type a FEN");
\r
4569 case IDM_Training:
\r
4573 case IDM_ShowGameList:
\r
4574 ShowGameListProc();
\r
4577 case IDM_EditTags:
\r
4581 case IDM_EditComment:
\r
4582 if (commentUp && editComment) {
\r
4585 EditCommentEvent();
\r
4605 case IDM_CallFlag:
\r
4625 case IDM_StopObserving:
\r
4626 StopObservingEvent();
\r
4629 case IDM_StopExamining:
\r
4630 StopExaminingEvent();
\r
4634 UploadGameEvent();
\r
4637 case IDM_TypeInMove:
\r
4638 PopUpMoveDialog('\000');
\r
4641 case IDM_TypeInName:
\r
4642 PopUpNameDialog('\000');
\r
4645 case IDM_Backward:
\r
4647 SetFocus(hwndMain);
\r
4654 SetFocus(hwndMain);
\r
4659 SetFocus(hwndMain);
\r
4664 SetFocus(hwndMain);
\r
4671 case IDM_TruncateGame:
\r
4672 TruncateGameEvent();
\r
4679 case IDM_RetractMove:
\r
4680 RetractMoveEvent();
\r
4683 case IDM_FlipView:
\r
4684 flipView = !flipView;
\r
4685 DrawPosition(FALSE, NULL);
\r
4688 case IDM_FlipClock:
\r
4689 flipClock = !flipClock;
\r
4690 DisplayBothClocks();
\r
4691 DrawPosition(FALSE, NULL);
\r
4694 case IDM_MuteSounds:
\r
4695 mute = !mute; // [HGM] mute: keep track of global muting variable
\r
4696 CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds,
\r
4697 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));
\r
4700 case IDM_GeneralOptions:
\r
4701 GeneralOptionsPopup(hwnd);
\r
4702 DrawPosition(TRUE, NULL);
\r
4705 case IDM_BoardOptions:
\r
4706 BoardOptionsPopup(hwnd);
\r
4709 case IDM_EnginePlayOptions:
\r
4710 EnginePlayOptionsPopup(hwnd);
\r
4713 case IDM_Engine1Options:
\r
4714 EngineOptionsPopup(hwnd, &first);
\r
4717 case IDM_Engine2Options:
\r
4718 EngineOptionsPopup(hwnd, &second);
\r
4721 case IDM_OptionsUCI:
\r
4722 UciOptionsPopup(hwnd);
\r
4725 case IDM_IcsOptions:
\r
4726 IcsOptionsPopup(hwnd);
\r
4730 FontsOptionsPopup(hwnd);
\r
4734 SoundOptionsPopup(hwnd);
\r
4737 case IDM_CommPort:
\r
4738 CommPortOptionsPopup(hwnd);
\r
4741 case IDM_LoadOptions:
\r
4742 LoadOptionsPopup(hwnd);
\r
4745 case IDM_SaveOptions:
\r
4746 SaveOptionsPopup(hwnd);
\r
4749 case IDM_TimeControl:
\r
4750 TimeControlOptionsPopup(hwnd);
\r
4753 case IDM_SaveSettings:
\r
4754 SaveSettings(settingsFileName);
\r
4757 case IDM_SaveSettingsOnExit:
\r
4758 saveSettingsOnExit = !saveSettingsOnExit;
\r
4759 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
\r
4760 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
4761 MF_CHECKED : MF_UNCHECKED));
\r
4772 case IDM_AboutGame:
\r
4777 appData.debugMode = !appData.debugMode;
\r
4778 if (appData.debugMode) {
\r
4779 char dir[MSG_SIZ];
\r
4780 GetCurrentDirectory(MSG_SIZ, dir);
\r
4781 SetCurrentDirectory(installDir);
\r
4782 debugFP = fopen(appData.nameOfDebugFile, "w");
\r
4783 SetCurrentDirectory(dir);
\r
4784 setbuf(debugFP, NULL);
\r
4791 case IDM_HELPCONTENTS:
\r
4792 if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&
\r
4793 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4794 MessageBox (GetFocus(),
\r
4795 "Unable to activate help",
\r
4796 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4800 case IDM_HELPSEARCH:
\r
4801 if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&
\r
4802 !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {
\r
4803 MessageBox (GetFocus(),
\r
4804 "Unable to activate help",
\r
4805 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4809 case IDM_HELPHELP:
\r
4810 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
\r
4811 MessageBox (GetFocus(),
\r
4812 "Unable to activate help",
\r
4813 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
4818 lpProc = MakeProcInstance((FARPROC)About, hInst);
\r
4820 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
\r
4821 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
\r
4822 FreeProcInstance(lpProc);
\r
4825 case IDM_DirectCommand1:
\r
4826 AskQuestionEvent("Direct Command",
\r
4827 "Send to chess program:", "", "1");
\r
4829 case IDM_DirectCommand2:
\r
4830 AskQuestionEvent("Direct Command",
\r
4831 "Send to second chess program:", "", "2");
\r
4834 case EP_WhitePawn:
\r
4835 EditPositionMenuEvent(WhitePawn, fromX, fromY);
\r
4836 fromX = fromY = -1;
\r
4839 case EP_WhiteKnight:
\r
4840 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
\r
4841 fromX = fromY = -1;
\r
4844 case EP_WhiteBishop:
\r
4845 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
\r
4846 fromX = fromY = -1;
\r
4849 case EP_WhiteRook:
\r
4850 EditPositionMenuEvent(WhiteRook, fromX, fromY);
\r
4851 fromX = fromY = -1;
\r
4854 case EP_WhiteQueen:
\r
4855 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
\r
4856 fromX = fromY = -1;
\r
4859 case EP_WhiteFerz:
\r
4860 EditPositionMenuEvent(WhiteFerz, fromX, fromY);
\r
4861 fromX = fromY = -1;
\r
4864 case EP_WhiteWazir:
\r
4865 EditPositionMenuEvent(WhiteWazir, fromX, fromY);
\r
4866 fromX = fromY = -1;
\r
4869 case EP_WhiteAlfil:
\r
4870 EditPositionMenuEvent(WhiteAlfil, fromX, fromY);
\r
4871 fromX = fromY = -1;
\r
4874 case EP_WhiteCannon:
\r
4875 EditPositionMenuEvent(WhiteCannon, fromX, fromY);
\r
4876 fromX = fromY = -1;
\r
4879 case EP_WhiteCardinal:
\r
4880 EditPositionMenuEvent(WhiteAngel, fromX, fromY);
\r
4881 fromX = fromY = -1;
\r
4884 case EP_WhiteMarshall:
\r
4885 EditPositionMenuEvent(WhiteMarshall, fromX, fromY);
\r
4886 fromX = fromY = -1;
\r
4889 case EP_WhiteKing:
\r
4890 EditPositionMenuEvent(WhiteKing, fromX, fromY);
\r
4891 fromX = fromY = -1;
\r
4894 case EP_BlackPawn:
\r
4895 EditPositionMenuEvent(BlackPawn, fromX, fromY);
\r
4896 fromX = fromY = -1;
\r
4899 case EP_BlackKnight:
\r
4900 EditPositionMenuEvent(BlackKnight, fromX, fromY);
\r
4901 fromX = fromY = -1;
\r
4904 case EP_BlackBishop:
\r
4905 EditPositionMenuEvent(BlackBishop, fromX, fromY);
\r
4906 fromX = fromY = -1;
\r
4909 case EP_BlackRook:
\r
4910 EditPositionMenuEvent(BlackRook, fromX, fromY);
\r
4911 fromX = fromY = -1;
\r
4914 case EP_BlackQueen:
\r
4915 EditPositionMenuEvent(BlackQueen, fromX, fromY);
\r
4916 fromX = fromY = -1;
\r
4919 case EP_BlackFerz:
\r
4920 EditPositionMenuEvent(BlackFerz, fromX, fromY);
\r
4921 fromX = fromY = -1;
\r
4924 case EP_BlackWazir:
\r
4925 EditPositionMenuEvent(BlackWazir, fromX, fromY);
\r
4926 fromX = fromY = -1;
\r
4929 case EP_BlackAlfil:
\r
4930 EditPositionMenuEvent(BlackAlfil, fromX, fromY);
\r
4931 fromX = fromY = -1;
\r
4934 case EP_BlackCannon:
\r
4935 EditPositionMenuEvent(BlackCannon, fromX, fromY);
\r
4936 fromX = fromY = -1;
\r
4939 case EP_BlackCardinal:
\r
4940 EditPositionMenuEvent(BlackAngel, fromX, fromY);
\r
4941 fromX = fromY = -1;
\r
4944 case EP_BlackMarshall:
\r
4945 EditPositionMenuEvent(BlackMarshall, fromX, fromY);
\r
4946 fromX = fromY = -1;
\r
4949 case EP_BlackKing:
\r
4950 EditPositionMenuEvent(BlackKing, fromX, fromY);
\r
4951 fromX = fromY = -1;
\r
4954 case EP_EmptySquare:
\r
4955 EditPositionMenuEvent(EmptySquare, fromX, fromY);
\r
4956 fromX = fromY = -1;
\r
4959 case EP_ClearBoard:
\r
4960 EditPositionMenuEvent(ClearBoard, fromX, fromY);
\r
4961 fromX = fromY = -1;
\r
4965 EditPositionMenuEvent(WhitePlay, fromX, fromY);
\r
4966 fromX = fromY = -1;
\r
4970 EditPositionMenuEvent(BlackPlay, fromX, fromY);
\r
4971 fromX = fromY = -1;
\r
4975 EditPositionMenuEvent(PromotePiece, fromX, fromY);
\r
4976 fromX = fromY = -1;
\r
4980 EditPositionMenuEvent(DemotePiece, fromX, fromY);
\r
4981 fromX = fromY = -1;
\r
4985 DropMenuEvent(WhitePawn, fromX, fromY);
\r
4986 fromX = fromY = -1;
\r
4990 DropMenuEvent(WhiteKnight, fromX, fromY);
\r
4991 fromX = fromY = -1;
\r
4995 DropMenuEvent(WhiteBishop, fromX, fromY);
\r
4996 fromX = fromY = -1;
\r
5000 DropMenuEvent(WhiteRook, fromX, fromY);
\r
5001 fromX = fromY = -1;
\r
5005 DropMenuEvent(WhiteQueen, fromX, fromY);
\r
5006 fromX = fromY = -1;
\r
5010 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5016 case CLOCK_TIMER_ID:
\r
5017 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
\r
5018 clockTimerEvent = 0;
\r
5019 DecrementClocks(); /* call into back end */
\r
5021 case LOAD_GAME_TIMER_ID:
\r
5022 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
\r
5023 loadGameTimerEvent = 0;
\r
5024 AutoPlayGameLoop(); /* call into back end */
\r
5026 case ANALYSIS_TIMER_ID:
\r
5027 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
\r
5028 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
\r
5029 AnalysisPeriodicEvent(0);
\r
5031 KillTimer(hwnd, analysisTimerEvent);
\r
5032 analysisTimerEvent = 0;
\r
5035 case DELAYED_TIMER_ID:
\r
5036 KillTimer(hwnd, delayedTimerEvent);
\r
5037 delayedTimerEvent = 0;
\r
5038 delayedTimerCallback();
\r
5043 case WM_USER_Input:
\r
5044 InputEvent(hwnd, message, wParam, lParam);
\r
5047 /* [AS] Also move "attached" child windows */
\r
5048 case WM_WINDOWPOSCHANGING:
\r
5050 if( hwnd == hwndMain && appData.useStickyWindows ) {
\r
5051 LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;
\r
5053 if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {
\r
5054 /* Window is moving */
\r
5057 // GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old
\r
5058 rcMain.left = wpMain.x; // replace by these 4 lines to reconstruct old rect
\r
5059 rcMain.right = wpMain.x + wpMain.width;
\r
5060 rcMain.top = wpMain.y;
\r
5061 rcMain.bottom = wpMain.y + wpMain.height;
\r
5063 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );
\r
5064 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );
\r
5065 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );
\r
5066 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );
\r
5067 ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );
\r
5068 wpMain.x = lpwp->x;
\r
5069 wpMain.y = lpwp->y;
\r
5074 /* [AS] Snapping */
\r
5075 case WM_ENTERSIZEMOVE:
\r
5076 if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }
\r
5077 if (hwnd == hwndMain) {
\r
5078 doingSizing = TRUE;
\r
5081 return OnEnterSizeMove( &sd, hwnd, wParam, lParam );
\r
5085 if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }
\r
5086 if (hwnd == hwndMain) {
\r
5087 lastSizing = wParam;
\r
5092 if(appData.debugMode) { fprintf(debugFP, "moving\n"); }
\r
5093 return OnMoving( &sd, hwnd, wParam, lParam );
\r
5095 case WM_EXITSIZEMOVE:
\r
5096 if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }
\r
5097 if (hwnd == hwndMain) {
\r
5099 doingSizing = FALSE;
\r
5100 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5101 GetClientRect(hwnd, &client);
\r
5102 ResizeBoard(client.right, client.bottom, lastSizing);
\r
5104 if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }
\r
5106 return OnExitSizeMove( &sd, hwnd, wParam, lParam );
\r
5109 case WM_DESTROY: /* message: window being destroyed */
\r
5110 PostQuitMessage(0);
\r
5114 if (hwnd == hwndMain) {
\r
5119 default: /* Passes it on if unprocessed */
\r
5120 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
5125 /*---------------------------------------------------------------------------*\
\r
5127 * Misc utility routines
\r
5129 \*---------------------------------------------------------------------------*/
\r
5132 * Decent random number generator, at least not as bad as Windows
\r
5133 * standard rand, which returns a value in the range 0 to 0x7fff.
\r
5135 unsigned int randstate;
\r
5140 randstate = randstate * 1664525 + 1013904223;
\r
5141 return (int) randstate & 0x7fffffff;
\r
5145 mysrandom(unsigned int seed)
\r
5152 * returns TRUE if user selects a different color, FALSE otherwise
\r
5156 ChangeColor(HWND hwnd, COLORREF *which)
\r
5158 static BOOL firstTime = TRUE;
\r
5159 static DWORD customColors[16];
\r
5161 COLORREF newcolor;
\r
5166 /* Make initial colors in use available as custom colors */
\r
5167 /* Should we put the compiled-in defaults here instead? */
\r
5169 customColors[i++] = lightSquareColor & 0xffffff;
\r
5170 customColors[i++] = darkSquareColor & 0xffffff;
\r
5171 customColors[i++] = whitePieceColor & 0xffffff;
\r
5172 customColors[i++] = blackPieceColor & 0xffffff;
\r
5173 customColors[i++] = highlightSquareColor & 0xffffff;
\r
5174 customColors[i++] = premoveHighlightColor & 0xffffff;
\r
5176 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
\r
5177 customColors[i++] = textAttribs[ccl].color;
\r
5179 while (i < 16) customColors[i++] = RGB(255, 255, 255);
\r
5180 firstTime = FALSE;
\r
5183 cc.lStructSize = sizeof(cc);
\r
5184 cc.hwndOwner = hwnd;
\r
5185 cc.hInstance = NULL;
\r
5186 cc.rgbResult = (DWORD) (*which & 0xffffff);
\r
5187 cc.lpCustColors = (LPDWORD) customColors;
\r
5188 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
\r
5190 if (!ChooseColor(&cc)) return FALSE;
\r
5192 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
\r
5193 if (newcolor == *which) return FALSE;
\r
5194 *which = newcolor;
\r
5198 InitDrawingColors();
\r
5199 InvalidateRect(hwnd, &boardRect, FALSE);
\r
5204 MyLoadSound(MySound *ms)
\r
5210 if (ms->data) free(ms->data);
\r
5213 switch (ms->name[0]) {
\r
5219 /* System sound from Control Panel. Don't preload here. */
\r
5223 if (ms->name[1] == NULLCHAR) {
\r
5224 /* "!" alone = silence */
\r
5227 /* Builtin wave resource. Error if not found. */
\r
5228 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
\r
5229 if (h == NULL) break;
\r
5230 ms->data = (void *)LoadResource(hInst, h);
\r
5231 if (h == NULL) break;
\r
5236 /* .wav file. Error if not found. */
\r
5237 f = fopen(ms->name, "rb");
\r
5238 if (f == NULL) break;
\r
5239 if (fstat(fileno(f), &st) < 0) break;
\r
5240 ms->data = malloc(st.st_size);
\r
5241 if (fread(ms->data, st.st_size, 1, f) < 1) break;
\r
5247 char buf[MSG_SIZ];
\r
5248 sprintf(buf, "Error loading sound %s", ms->name);
\r
5249 DisplayError(buf, GetLastError());
\r
5255 MyPlaySound(MySound *ms)
\r
5257 BOOLEAN ok = FALSE;
\r
5259 if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted
\r
5260 switch (ms->name[0]) {
\r
5262 if(appData.debugMode) fprintf(debugFP, "silence\n");
\r
5267 /* System sound from Control Panel (deprecated feature).
\r
5268 "$" alone or an unset sound name gets default beep (still in use). */
\r
5269 if (ms->name[1]) {
\r
5270 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
\r
5272 if (!ok) ok = MessageBeep(MB_OK);
\r
5275 /* Builtin wave resource, or "!" alone for silence */
\r
5276 if (ms->name[1]) {
\r
5277 if (ms->data == NULL) return FALSE;
\r
5278 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5284 /* .wav file. Error if not found. */
\r
5285 if (ms->data == NULL) return FALSE;
\r
5286 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
\r
5289 /* Don't print an error: this can happen innocently if the sound driver
\r
5290 is busy; for instance, if another instance of WinBoard is playing
\r
5291 a sound at about the same time. */
\r
5297 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5300 OPENFILENAME *ofn;
\r
5301 static UINT *number; /* gross that this is static */
\r
5303 switch (message) {
\r
5304 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5305 /* Center the dialog over the application window */
\r
5306 ofn = (OPENFILENAME *) lParam;
\r
5307 if (ofn->Flags & OFN_ENABLETEMPLATE) {
\r
5308 number = (UINT *) ofn->lCustData;
\r
5309 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
\r
5313 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5314 return FALSE; /* Allow for further processing */
\r
5317 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
\r
5318 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
\r
5320 return FALSE; /* Allow for further processing */
\r
5326 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
\r
5328 static UINT *number;
\r
5329 OPENFILENAME *ofname;
\r
5332 case WM_INITDIALOG:
\r
5333 ofname = (OPENFILENAME *)lParam;
\r
5334 number = (UINT *)(ofname->lCustData);
\r
5337 ofnot = (OFNOTIFY *)lParam;
\r
5338 if (ofnot->hdr.code == CDN_FILEOK) {
\r
5339 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
\r
5348 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string
\r
5349 char *nameFilt, char *dlgTitle, UINT *number,
\r
5350 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
\r
5352 OPENFILENAME openFileName;
\r
5353 char buf1[MSG_SIZ];
\r
5356 if (fileName == NULL) fileName = buf1;
\r
5357 if (defName == NULL) {
\r
5358 strcpy(fileName, "*.");
\r
5359 strcat(fileName, defExt);
\r
5361 strcpy(fileName, defName);
\r
5363 if (fileTitle) strcpy(fileTitle, "");
\r
5364 if (number) *number = 0;
\r
5366 openFileName.lStructSize = sizeof(OPENFILENAME);
\r
5367 openFileName.hwndOwner = hwnd;
\r
5368 openFileName.hInstance = (HANDLE) hInst;
\r
5369 openFileName.lpstrFilter = nameFilt;
\r
5370 openFileName.lpstrCustomFilter = (LPSTR) NULL;
\r
5371 openFileName.nMaxCustFilter = 0L;
\r
5372 openFileName.nFilterIndex = 1L;
\r
5373 openFileName.lpstrFile = fileName;
\r
5374 openFileName.nMaxFile = MSG_SIZ;
\r
5375 openFileName.lpstrFileTitle = fileTitle;
\r
5376 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
\r
5377 openFileName.lpstrInitialDir = NULL;
\r
5378 openFileName.lpstrTitle = dlgTitle;
\r
5379 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
\r
5380 | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST)
\r
5381 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
\r
5382 | (oldDialog ? 0 : OFN_EXPLORER);
\r
5383 openFileName.nFileOffset = 0;
\r
5384 openFileName.nFileExtension = 0;
\r
5385 openFileName.lpstrDefExt = defExt;
\r
5386 openFileName.lCustData = (LONG) number;
\r
5387 openFileName.lpfnHook = oldDialog ?
\r
5388 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
\r
5389 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
\r
5391 if (write[0] != 'r' ? GetSaveFileName(&openFileName) :
\r
5392 GetOpenFileName(&openFileName)) {
\r
5393 /* open the file */
\r
5394 f = fopen(openFileName.lpstrFile, write);
\r
5396 MessageBox(hwnd, "File open failed", NULL,
\r
5397 MB_OK|MB_ICONEXCLAMATION);
\r
5401 int err = CommDlgExtendedError();
\r
5402 if (err != 0) DisplayError("Internal error in file dialog box", err);
\r
5411 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
\r
5413 HMENU hmenuTrackPopup; /* floating pop-up menu */
\r
5416 * Get the first pop-up menu in the menu template. This is the
\r
5417 * menu that TrackPopupMenu displays.
\r
5419 hmenuTrackPopup = GetSubMenu(hmenu, 0);
\r
5421 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
\r
5424 * TrackPopup uses screen coordinates, so convert the
\r
5425 * coordinates of the mouse click to screen coordinates.
\r
5427 ClientToScreen(hwnd, (LPPOINT) &pt);
\r
5429 /* Draw and track the floating pop-up menu. */
\r
5430 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
\r
5431 pt.x, pt.y, 0, hwnd, NULL);
\r
5433 /* Destroy the menu.*/
\r
5434 DestroyMenu(hmenu);
\r
5439 int sizeX, sizeY, newSizeX, newSizeY;
\r
5441 } ResizeEditPlusButtonsClosure;
\r
5444 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
\r
5446 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
\r
5450 if (hChild == cl->hText) return TRUE;
\r
5451 GetWindowRect(hChild, &rect); /* gives screen coords */
\r
5452 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
\r
5453 pt.y = rect.top + cl->newSizeY - cl->sizeY;
\r
5454 ScreenToClient(cl->hDlg, &pt);
\r
5455 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
\r
5456 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
\r
5460 /* Resize a dialog that has a (rich) edit field filling most of
\r
5461 the top, with a row of buttons below */
\r
5463 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
\r
5466 int newTextHeight, newTextWidth;
\r
5467 ResizeEditPlusButtonsClosure cl;
\r
5469 /*if (IsIconic(hDlg)) return;*/
\r
5470 if (newSizeX == sizeX && newSizeY == sizeY) return;
\r
5472 cl.hdwp = BeginDeferWindowPos(8);
\r
5474 GetWindowRect(hText, &rectText); /* gives screen coords */
\r
5475 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
5476 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
5477 if (newTextHeight < 0) {
\r
5478 newSizeY += -newTextHeight;
\r
5479 newTextHeight = 0;
\r
5481 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
\r
5482 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
5488 cl.newSizeX = newSizeX;
\r
5489 cl.newSizeY = newSizeY;
\r
5490 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
\r
5492 EndDeferWindowPos(cl.hdwp);
\r
5495 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)
\r
5497 RECT rChild, rParent;
\r
5498 int wChild, hChild, wParent, hParent;
\r
5499 int wScreen, hScreen, xNew, yNew;
\r
5502 /* Get the Height and Width of the child window */
\r
5503 GetWindowRect (hwndChild, &rChild);
\r
5504 wChild = rChild.right - rChild.left;
\r
5505 hChild = rChild.bottom - rChild.top;
\r
5507 /* Get the Height and Width of the parent window */
\r
5508 GetWindowRect (hwndParent, &rParent);
\r
5509 wParent = rParent.right - rParent.left;
\r
5510 hParent = rParent.bottom - rParent.top;
\r
5512 /* Get the display limits */
\r
5513 hdc = GetDC (hwndChild);
\r
5514 wScreen = GetDeviceCaps (hdc, HORZRES);
\r
5515 hScreen = GetDeviceCaps (hdc, VERTRES);
\r
5516 ReleaseDC(hwndChild, hdc);
\r
5518 /* Calculate new X position, then adjust for screen */
\r
5519 xNew = rParent.left + ((wParent - wChild) /2);
\r
5522 } else if ((xNew+wChild) > wScreen) {
\r
5523 xNew = wScreen - wChild;
\r
5526 /* Calculate new Y position, then adjust for screen */
\r
5528 yNew = rParent.top + ((hParent - hChild) /2);
\r
5531 yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;
\r
5536 } else if ((yNew+hChild) > hScreen) {
\r
5537 yNew = hScreen - hChild;
\r
5540 /* Set it, and return */
\r
5541 return SetWindowPos (hwndChild, NULL,
\r
5542 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
5545 /* Center one window over another */
\r
5546 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
\r
5548 return CenterWindowEx( hwndChild, hwndParent, 0 );
\r
5551 /*---------------------------------------------------------------------------*\
\r
5553 * Startup Dialog functions
\r
5555 \*---------------------------------------------------------------------------*/
\r
5557 InitComboStrings(HANDLE hwndCombo, char **cd)
\r
5559 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5561 while (*cd != NULL) {
\r
5562 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
\r
5568 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
\r
5570 char buf1[MAX_ARG_LEN];
\r
5573 if (str[0] == '@') {
\r
5574 FILE* f = fopen(str + 1, "r");
\r
5576 DisplayFatalError(str + 1, errno, 2);
\r
5579 len = fread(buf1, 1, sizeof(buf1)-1, f);
\r
5581 buf1[len] = NULLCHAR;
\r
5585 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
\r
5588 char buf[MSG_SIZ];
\r
5589 char *end = strchr(str, '\n');
\r
5590 if (end == NULL) return;
\r
5591 memcpy(buf, str, end - str);
\r
5592 buf[end - str] = NULLCHAR;
\r
5593 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
\r
5599 SetStartupDialogEnables(HWND hDlg)
\r
5601 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5602 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5603 (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));
\r
5604 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5605 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
\r
5606 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
\r
5607 IsDlgButtonChecked(hDlg, OPT_ChessServer));
\r
5608 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
\r
5609 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
\r
5610 EnableWindow(GetDlgItem(hDlg, IDOK),
\r
5611 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
\r
5612 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
\r
5613 IsDlgButtonChecked(hDlg, OPT_View));
\r
5617 QuoteForFilename(char *filename)
\r
5619 int dquote, space;
\r
5620 dquote = strchr(filename, '"') != NULL;
\r
5621 space = strchr(filename, ' ') != NULL;
\r
5622 if (dquote || space) {
\r
5634 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
\r
5636 char buf[MSG_SIZ];
\r
5639 InitComboStringsFromOption(hwndCombo, nthnames);
\r
5640 q = QuoteForFilename(nthcp);
\r
5641 sprintf(buf, "%s%s%s", q, nthcp, q);
\r
5642 if (*nthdir != NULLCHAR) {
\r
5643 q = QuoteForFilename(nthdir);
\r
5644 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
\r
5646 if (*nthcp == NULLCHAR) {
\r
5647 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5648 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5649 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5650 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5655 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5657 char buf[MSG_SIZ];
\r
5661 switch (message) {
\r
5662 case WM_INITDIALOG:
\r
5663 /* Center the dialog */
\r
5664 CenterWindow (hDlg, GetDesktopWindow());
\r
5665 /* Initialize the dialog items */
\r
5666 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
\r
5667 appData.firstChessProgram, "fd", appData.firstDirectory,
\r
5668 firstChessProgramNames);
\r
5669 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
\r
5670 appData.secondChessProgram, "sd", appData.secondDirectory,
\r
5671 secondChessProgramNames);
\r
5672 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
\r
5673 InitComboStringsFromOption(hwndCombo, icsNames);
\r
5674 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
\r
5675 if (*appData.icsHelper != NULLCHAR) {
\r
5676 char *q = QuoteForFilename(appData.icsHelper);
\r
5677 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
\r
5679 if (*appData.icsHost == NULLCHAR) {
\r
5680 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
\r
5681 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
\r
5682 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
\r
5683 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
\r
5684 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
\r
5687 if (appData.icsActive) {
\r
5688 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
\r
5690 else if (appData.noChessProgram) {
\r
5691 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
\r
5694 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
\r
5697 SetStartupDialogEnables(hDlg);
\r
5701 switch (LOWORD(wParam)) {
\r
5703 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
\r
5704 strcpy(buf, "/fcp=");
\r
5705 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5707 ParseArgs(StringGet, &p);
\r
5708 strcpy(buf, "/scp=");
\r
5709 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5711 ParseArgs(StringGet, &p);
\r
5712 appData.noChessProgram = FALSE;
\r
5713 appData.icsActive = FALSE;
\r
5714 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
\r
5715 strcpy(buf, "/ics /icshost=");
\r
5716 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5718 ParseArgs(StringGet, &p);
\r
5719 if (appData.zippyPlay) {
\r
5720 strcpy(buf, "/fcp=");
\r
5721 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
\r
5723 ParseArgs(StringGet, &p);
\r
5725 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
\r
5726 appData.noChessProgram = TRUE;
\r
5727 appData.icsActive = FALSE;
\r
5729 MessageBox(hDlg, "Choose an option, or cancel to exit",
\r
5730 "Option Error", MB_OK|MB_ICONEXCLAMATION);
\r
5733 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
\r
5734 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
\r
5736 ParseArgs(StringGet, &p);
\r
5738 EndDialog(hDlg, TRUE);
\r
5745 case IDM_HELPCONTENTS:
\r
5746 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
\r
5747 MessageBox (GetFocus(),
\r
5748 "Unable to activate help",
\r
5749 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
\r
5754 SetStartupDialogEnables(hDlg);
\r
5762 /*---------------------------------------------------------------------------*\
\r
5764 * About box dialog functions
\r
5766 \*---------------------------------------------------------------------------*/
\r
5768 /* Process messages for "About" dialog box */
\r
5770 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5772 switch (message) {
\r
5773 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5774 /* Center the dialog over the application window */
\r
5775 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
\r
5776 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
\r
5780 case WM_COMMAND: /* message: received a command */
\r
5781 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
\r
5782 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
\r
5783 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
5791 /*---------------------------------------------------------------------------*\
\r
5793 * Comment Dialog functions
\r
5795 \*---------------------------------------------------------------------------*/
\r
5798 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5800 static HANDLE hwndText = NULL;
\r
5801 int len, newSizeX, newSizeY, flags;
\r
5802 static int sizeX, sizeY;
\r
5807 switch (message) {
\r
5808 case WM_INITDIALOG: /* message: initialize dialog box */
\r
5809 /* Initialize the dialog items */
\r
5810 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5811 SetDlgItemText(hDlg, OPT_CommentText, commentText);
\r
5812 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
\r
5813 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
\r
5814 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
\r
5815 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
\r
5816 SetWindowText(hDlg, commentTitle);
\r
5817 if (editComment) {
\r
5818 SetFocus(hwndText);
\r
5820 SetFocus(GetDlgItem(hDlg, IDOK));
\r
5822 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
\r
5823 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
\r
5824 MAKELPARAM(FALSE, 0));
\r
5825 /* Size and position the dialog */
\r
5826 if (!commentDialog) {
\r
5827 commentDialog = hDlg;
\r
5828 flags = SWP_NOZORDER;
\r
5829 GetClientRect(hDlg, &rect);
\r
5830 sizeX = rect.right;
\r
5831 sizeY = rect.bottom;
\r
5832 if (wpComment.x != CW_USEDEFAULT && wpComment.y != CW_USEDEFAULT &&
\r
5833 wpComment.width != CW_USEDEFAULT && wpComment.height != CW_USEDEFAULT) {
\r
5834 WINDOWPLACEMENT wp;
\r
5835 EnsureOnScreen(&wpComment.x, &wpComment.y, 0, 0);
\r
5836 wp.length = sizeof(WINDOWPLACEMENT);
\r
5838 wp.showCmd = SW_SHOW;
\r
5839 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
5840 wp.rcNormalPosition.left = wpComment.x;
\r
5841 wp.rcNormalPosition.right = wpComment.x + wpComment.width;
\r
5842 wp.rcNormalPosition.top = wpComment.y;
\r
5843 wp.rcNormalPosition.bottom = wpComment.y + wpComment.height;
\r
5844 SetWindowPlacement(hDlg, &wp);
\r
5846 GetClientRect(hDlg, &rect);
\r
5847 newSizeX = rect.right;
\r
5848 newSizeY = rect.bottom;
\r
5849 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
\r
5850 newSizeX, newSizeY);
\r
5857 case WM_COMMAND: /* message: received a command */
\r
5858 switch (LOWORD(wParam)) {
\r
5860 if (editComment) {
\r
5862 /* Read changed options from the dialog box */
\r
5863 hwndText = GetDlgItem(hDlg, OPT_CommentText);
\r
5864 len = GetWindowTextLength(hwndText);
\r
5865 str = (char *) malloc(len + 1);
\r
5866 GetWindowText(hwndText, str, len + 1);
\r
5875 ReplaceComment(commentIndex, str);
\r
5882 case OPT_CancelComment:
\r
5886 case OPT_ClearComment:
\r
5887 SetDlgItemText(hDlg, OPT_CommentText, "");
\r
5890 case OPT_EditComment:
\r
5891 EditCommentEvent();
\r
5900 newSizeX = LOWORD(lParam);
\r
5901 newSizeY = HIWORD(lParam);
\r
5902 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
\r
5907 case WM_GETMINMAXINFO:
\r
5908 /* Prevent resizing window too small */
\r
5909 mmi = (MINMAXINFO *) lParam;
\r
5910 mmi->ptMinTrackSize.x = 100;
\r
5911 mmi->ptMinTrackSize.y = 100;
\r
5918 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
\r
5923 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
\r
5925 if (str == NULL) str = "";
\r
5926 p = (char *) malloc(2 * strlen(str) + 2);
\r
5929 if (*str == '\n') *q++ = '\r';
\r
5933 if (commentText != NULL) free(commentText);
\r
5935 commentIndex = index;
\r
5936 commentTitle = title;
\r
5938 editComment = edit;
\r
5940 if (commentDialog) {
\r
5941 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
\r
5942 if (!commentUp) ShowWindow(commentDialog, SW_SHOW);
\r
5944 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
\r
5945 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
\r
5946 hwndMain, (DLGPROC)lpProc);
\r
5947 FreeProcInstance(lpProc);
\r
5953 /*---------------------------------------------------------------------------*\
\r
5955 * Type-in move dialog functions
\r
5957 \*---------------------------------------------------------------------------*/
\r
5960 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
5962 char move[MSG_SIZ];
\r
5964 ChessMove moveType;
\r
5965 int fromX, fromY, toX, toY;
\r
5968 switch (message) {
\r
5969 case WM_INITDIALOG:
\r
5970 move[0] = (char) lParam;
\r
5971 move[1] = NULLCHAR;
\r
5972 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
5973 hInput = GetDlgItem(hDlg, OPT_Move);
\r
5974 SetWindowText(hInput, move);
\r
5976 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
5980 switch (LOWORD(wParam)) {
\r
5982 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
\r
5983 { int n; Board board;
\r
5985 if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
\r
5986 EditPositionPasteFEN(move);
\r
5987 EndDialog(hDlg, TRUE);
\r
5990 // [HGM] movenum: allow move number to be typed in any mode
\r
5991 if(sscanf(move, "%d", &n) == 1 && n != 0 ) {
\r
5993 EndDialog(hDlg, TRUE);
\r
5997 if (gameMode != EditGame && currentMove != forwardMostMove &&
\r
5998 gameMode != Training) {
\r
5999 DisplayMoveError("Displayed move is not current");
\r
6001 // GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream
\r
6002 int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6003 &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
\r
6004 if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
\r
6005 if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
\r
6006 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
\r
6007 if (gameMode != Training)
\r
6008 forwardMostMove = currentMove;
\r
6009 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
6011 DisplayMoveError("Could not parse move");
\r
6014 EndDialog(hDlg, TRUE);
\r
6017 EndDialog(hDlg, FALSE);
\r
6028 PopUpMoveDialog(char firstchar)
\r
6032 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
\r
6033 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
\r
6034 gameMode == AnalyzeMode || gameMode == EditGame ||
\r
6035 gameMode == EditPosition || gameMode == IcsExamining ||
\r
6036 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
\r
6037 isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
\r
6038 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||
\r
6039 gameMode == IcsObserving || gameMode == TwoMachinesPlay ) ||
\r
6040 gameMode == Training) {
\r
6041 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
\r
6042 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
\r
6043 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6044 FreeProcInstance(lpProc);
\r
6048 /*---------------------------------------------------------------------------*\
\r
6050 * Type-in name dialog functions
\r
6052 \*---------------------------------------------------------------------------*/
\r
6055 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6057 char move[MSG_SIZ];
\r
6060 switch (message) {
\r
6061 case WM_INITDIALOG:
\r
6062 move[0] = (char) lParam;
\r
6063 move[1] = NULLCHAR;
\r
6064 CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );
\r
6065 hInput = GetDlgItem(hDlg, OPT_Name);
\r
6066 SetWindowText(hInput, move);
\r
6068 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
\r
6072 switch (LOWORD(wParam)) {
\r
6074 GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));
\r
6075 appData.userName = strdup(move);
\r
6078 EndDialog(hDlg, TRUE);
\r
6081 EndDialog(hDlg, FALSE);
\r
6092 PopUpNameDialog(char firstchar)
\r
6096 lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);
\r
6097 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),
\r
6098 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
\r
6099 FreeProcInstance(lpProc);
\r
6102 /*---------------------------------------------------------------------------*\
\r
6106 \*---------------------------------------------------------------------------*/
\r
6108 /* Nonmodal error box */
\r
6109 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
\r
6110 WPARAM wParam, LPARAM lParam);
\r
6113 ErrorPopUp(char *title, char *content)
\r
6117 BOOLEAN modal = hwndMain == NULL;
\r
6135 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6136 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6139 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
\r
6141 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
\r
6142 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6143 hwndMain, (DLGPROC)lpProc);
\r
6144 FreeProcInstance(lpProc);
\r
6151 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
\r
6152 if (errorDialog == NULL) return;
\r
6153 DestroyWindow(errorDialog);
\r
6154 errorDialog = NULL;
\r
6155 if(errorExitStatus) ExitEvent(errorExitStatus);
\r
6159 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6164 switch (message) {
\r
6165 case WM_INITDIALOG:
\r
6166 GetWindowRect(hDlg, &rChild);
\r
6169 SetWindowPos(hDlg, NULL, rChild.left,
\r
6170 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
\r
6171 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6175 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6176 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6177 and it doesn't work when you resize the dialog.
\r
6178 For now, just give it a default position.
\r
6180 SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
6182 errorDialog = hDlg;
\r
6183 SetWindowText(hDlg, errorTitle);
\r
6184 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6185 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6189 switch (LOWORD(wParam)) {
\r
6192 if (errorDialog == hDlg) errorDialog = NULL;
\r
6193 DestroyWindow(hDlg);
\r
6205 HWND gothicDialog = NULL;
\r
6208 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6212 int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);
\r
6214 switch (message) {
\r
6215 case WM_INITDIALOG:
\r
6216 GetWindowRect(hDlg, &rChild);
\r
6218 SetWindowPos(hDlg, NULL, wpMain.x, wpMain.y-height, wpMain.width, height,
\r
6222 [AS] It seems that the above code wants to move the dialog up in the "caption
\r
6223 area" of the main window, but it uses the dialog height as an hard-coded constant,
\r
6224 and it doesn't work when you resize the dialog.
\r
6225 For now, just give it a default position.
\r
6227 gothicDialog = hDlg;
\r
6228 SetWindowText(hDlg, errorTitle);
\r
6229 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
\r
6230 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
\r
6234 switch (LOWORD(wParam)) {
\r
6237 if (errorDialog == hDlg) errorDialog = NULL;
\r
6238 DestroyWindow(hDlg);
\r
6250 GothicPopUp(char *title, VariantClass variant)
\r
6253 static char *lastTitle;
\r
6255 strncpy(errorTitle, title, sizeof(errorTitle));
\r
6256 errorTitle[sizeof(errorTitle) - 1] = '\0';
\r
6258 if(lastTitle != title && gothicDialog != NULL) {
\r
6259 DestroyWindow(gothicDialog);
\r
6260 gothicDialog = NULL;
\r
6262 if(variant != VariantNormal && gothicDialog == NULL) {
\r
6263 title = lastTitle;
\r
6264 lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);
\r
6265 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
\r
6266 hwndMain, (DLGPROC)lpProc);
\r
6267 FreeProcInstance(lpProc);
\r
6272 /*---------------------------------------------------------------------------*\
\r
6274 * Ics Interaction console functions
\r
6276 \*---------------------------------------------------------------------------*/
\r
6278 #define HISTORY_SIZE 64
\r
6279 static char *history[HISTORY_SIZE];
\r
6280 int histIn = 0, histP = 0;
\r
6283 SaveInHistory(char *cmd)
\r
6285 if (history[histIn] != NULL) {
\r
6286 free(history[histIn]);
\r
6287 history[histIn] = NULL;
\r
6289 if (*cmd == NULLCHAR) return;
\r
6290 history[histIn] = StrSave(cmd);
\r
6291 histIn = (histIn + 1) % HISTORY_SIZE;
\r
6292 if (history[histIn] != NULL) {
\r
6293 free(history[histIn]);
\r
6294 history[histIn] = NULL;
\r
6300 PrevInHistory(char *cmd)
\r
6303 if (histP == histIn) {
\r
6304 if (history[histIn] != NULL) free(history[histIn]);
\r
6305 history[histIn] = StrSave(cmd);
\r
6307 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
\r
6308 if (newhp == histIn || history[newhp] == NULL) return NULL;
\r
6310 return history[histP];
\r
6316 if (histP == histIn) return NULL;
\r
6317 histP = (histP + 1) % HISTORY_SIZE;
\r
6318 return history[histP];
\r
6322 LoadIcsTextMenu(IcsTextMenuEntry *e)
\r
6326 hmenu = LoadMenu(hInst, "TextMenu");
\r
6327 h = GetSubMenu(hmenu, 0);
\r
6329 if (strcmp(e->item, "-") == 0) {
\r
6330 AppendMenu(h, MF_SEPARATOR, 0, 0);
\r
6331 } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)
\r
6332 int flags = MF_STRING, j = 0;
\r
6333 if (e->item[0] == '|') {
\r
6334 flags |= MF_MENUBARBREAK;
\r
6337 if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy
\r
6338 AppendMenu(h, flags, IDM_CommandX + i, e->item + j);
\r
6346 WNDPROC consoleTextWindowProc;
\r
6349 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
\r
6351 char buf[MSG_SIZ], name[MSG_SIZ];
\r
6352 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6356 SetWindowText(hInput, command);
\r
6358 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6360 sel.cpMin = 999999;
\r
6361 sel.cpMax = 999999;
\r
6362 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6367 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6368 if (sel.cpMin == sel.cpMax) {
\r
6369 /* Expand to surrounding word */
\r
6372 tr.chrg.cpMax = sel.cpMin;
\r
6373 tr.chrg.cpMin = --sel.cpMin;
\r
6374 if (sel.cpMin < 0) break;
\r
6375 tr.lpstrText = name;
\r
6376 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6377 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6381 tr.chrg.cpMin = sel.cpMax;
\r
6382 tr.chrg.cpMax = ++sel.cpMax;
\r
6383 tr.lpstrText = name;
\r
6384 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
\r
6385 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
\r
6388 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6389 MessageBeep(MB_ICONEXCLAMATION);
\r
6393 tr.lpstrText = name;
\r
6394 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
\r
6396 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
\r
6397 MessageBeep(MB_ICONEXCLAMATION);
\r
6400 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
\r
6403 sprintf(buf, "%s %s", command, name);
\r
6404 SetWindowText(hInput, buf);
\r
6405 SendMessage(hInput, WM_CHAR, '\r', 0);
\r
6407 if(!strcmp(command, "chat")) { ChatPopUp(name); return; }
\r
6408 sprintf(buf, "%s %s ", command, name); /* trailing space */
\r
6409 SetWindowText(hInput, buf);
\r
6410 sel.cpMin = 999999;
\r
6411 sel.cpMax = 999999;
\r
6412 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6418 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6423 switch (message) {
\r
6425 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6428 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
\r
6431 sel.cpMin = 999999;
\r
6432 sel.cpMax = 999999;
\r
6433 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6434 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
\r
6439 if(wParam != '\022') {
\r
6440 if (wParam == '\t') {
\r
6441 if (GetKeyState(VK_SHIFT) < 0) {
\r
6443 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6444 if (buttonDesc[0].hwnd) {
\r
6445 SetFocus(buttonDesc[0].hwnd);
\r
6447 SetFocus(hwndMain);
\r
6451 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
6454 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6455 JAWS_DELETE( SetFocus(hInput); )
\r
6456 SendMessage(hInput, message, wParam, lParam);
\r
6459 } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu
\r
6460 case WM_RBUTTONDOWN:
\r
6461 if (!(GetKeyState(VK_SHIFT) & ~1)) {
\r
6462 /* Move selection here if it was empty */
\r
6464 pt.x = LOWORD(lParam);
\r
6465 pt.y = HIWORD(lParam);
\r
6466 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6467 if (sel.cpMin == sel.cpMax) {
\r
6468 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
\r
6469 sel.cpMax = sel.cpMin;
\r
6470 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6472 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
\r
6473 { // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click
\r
6475 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
\r
6476 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6477 if (sel.cpMin == sel.cpMax) {
\r
6478 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6479 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
\r
6481 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6482 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6484 pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item
\r
6485 pt.y = HIWORD(lParam)-10; // make it appear as if mouse moved there, so it will be selected on up-click
\r
6486 PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);
\r
6487 MenuPopup(hwnd, pt, hmenu, -1);
\r
6491 case WM_RBUTTONUP:
\r
6492 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6493 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6494 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6498 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6500 return SendMessage(hInput, message, wParam, lParam);
\r
6501 case WM_MBUTTONDOWN:
\r
6502 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6504 switch (LOWORD(wParam)) {
\r
6505 case IDM_QuickPaste:
\r
6507 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6508 if (sel.cpMin == sel.cpMax) {
\r
6509 MessageBeep(MB_ICONEXCLAMATION);
\r
6512 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6513 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
6514 SendMessage(hInput, WM_PASTE, 0, 0);
\r
6519 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6522 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6525 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6529 int i = LOWORD(wParam) - IDM_CommandX;
\r
6530 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
\r
6531 icsTextMenuEntry[i].command != NULL) {
\r
6532 CommandX(hwnd, icsTextMenuEntry[i].command,
\r
6533 icsTextMenuEntry[i].getname,
\r
6534 icsTextMenuEntry[i].immediate);
\r
6542 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
\r
6545 WNDPROC consoleInputWindowProc;
\r
6548 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
6550 char buf[MSG_SIZ];
\r
6552 static BOOL sendNextChar = FALSE;
\r
6553 static BOOL quoteNextChar = FALSE;
\r
6554 InputSource *is = consoleInputSource;
\r
6558 switch (message) {
\r
6560 if (!appData.localLineEditing || sendNextChar) {
\r
6561 is->buf[0] = (CHAR) wParam;
\r
6563 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6564 sendNextChar = FALSE;
\r
6567 if (quoteNextChar) {
\r
6568 buf[0] = (char) wParam;
\r
6569 buf[1] = NULLCHAR;
\r
6570 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
\r
6571 quoteNextChar = FALSE;
\r
6575 case '\r': /* Enter key */
\r
6576 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
\r
6577 if (consoleEcho) SaveInHistory(is->buf);
\r
6578 is->buf[is->count++] = '\n';
\r
6579 is->buf[is->count] = NULLCHAR;
\r
6580 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
6581 if (consoleEcho) {
\r
6582 ConsoleOutput(is->buf, is->count, TRUE);
\r
6583 } else if (appData.localLineEditing) {
\r
6584 ConsoleOutput("\n", 1, TRUE);
\r
6587 case '\033': /* Escape key */
\r
6588 SetWindowText(hwnd, "");
\r
6589 cf.cbSize = sizeof(CHARFORMAT);
\r
6590 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
6591 if (consoleEcho) {
\r
6592 cf.crTextColor = textAttribs[ColorNormal].color;
\r
6594 cf.crTextColor = COLOR_ECHOOFF;
\r
6596 cf.dwEffects = textAttribs[ColorNormal].effects;
\r
6597 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
6599 case '\t': /* Tab key */
\r
6600 if (GetKeyState(VK_SHIFT) < 0) {
\r
6602 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
6605 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
\r
6606 if (buttonDesc[0].hwnd) {
\r
6607 SetFocus(buttonDesc[0].hwnd);
\r
6609 SetFocus(hwndMain);
\r
6613 case '\023': /* Ctrl+S */
\r
6614 sendNextChar = TRUE;
\r
6616 case '\021': /* Ctrl+Q */
\r
6617 quoteNextChar = TRUE;
\r
6627 GetWindowText(hwnd, buf, MSG_SIZ);
\r
6628 p = PrevInHistory(buf);
\r
6630 SetWindowText(hwnd, p);
\r
6631 sel.cpMin = 999999;
\r
6632 sel.cpMax = 999999;
\r
6633 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6638 p = NextInHistory();
\r
6640 SetWindowText(hwnd, p);
\r
6641 sel.cpMin = 999999;
\r
6642 sel.cpMax = 999999;
\r
6643 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6649 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
\r
6653 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
\r
6657 case WM_MBUTTONDOWN:
\r
6658 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6659 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6661 case WM_RBUTTONUP:
\r
6662 if (GetKeyState(VK_SHIFT) & ~1) {
\r
6663 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
\r
6664 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
\r
6668 hmenu = LoadMenu(hInst, "InputMenu");
\r
6669 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
6670 if (sel.cpMin == sel.cpMax) {
\r
6671 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
\r
6672 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
\r
6674 if (!IsClipboardFormatAvailable(CF_TEXT)) {
\r
6675 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
\r
6677 pt.x = LOWORD(lParam);
\r
6678 pt.y = HIWORD(lParam);
\r
6679 MenuPopup(hwnd, pt, hmenu, -1);
\r
6683 switch (LOWORD(wParam)) {
\r
6685 SendMessage(hwnd, EM_UNDO, 0, 0);
\r
6687 case IDM_SelectAll:
\r
6689 sel.cpMax = -1; /*999999?*/
\r
6690 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6693 SendMessage(hwnd, WM_CUT, 0, 0);
\r
6696 SendMessage(hwnd, WM_PASTE, 0, 0);
\r
6699 SendMessage(hwnd, WM_COPY, 0, 0);
\r
6704 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
\r
6707 #define CO_MAX 100000
\r
6708 #define CO_TRIM 1000
\r
6711 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
6713 static SnapData sd;
\r
6714 HWND hText, hInput;
\r
6716 static int sizeX, sizeY;
\r
6717 int newSizeX, newSizeY;
\r
6721 hText = GetDlgItem(hDlg, OPT_ConsoleText);
\r
6722 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
\r
6724 switch (message) {
\r
6726 if (((NMHDR*)lParam)->code == EN_LINK)
\r
6728 ENLINK *pLink = (ENLINK*)lParam;
\r
6729 if (pLink->msg == WM_LBUTTONUP)
\r
6733 tr.chrg = pLink->chrg;
\r
6734 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
6735 SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
6736 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
6737 free(tr.lpstrText);
\r
6741 case WM_INITDIALOG: /* message: initialize dialog box */
\r
6742 hwndConsole = hDlg;
\r
6744 consoleTextWindowProc = (WNDPROC)
\r
6745 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
\r
6746 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6747 consoleInputWindowProc = (WNDPROC)
\r
6748 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
\r
6749 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
6750 Colorize(ColorNormal, TRUE);
\r
6751 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
\r
6752 ChangedConsoleFont();
\r
6753 GetClientRect(hDlg, &rect);
\r
6754 sizeX = rect.right;
\r
6755 sizeY = rect.bottom;
\r
6756 if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&
\r
6757 wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {
\r
6758 WINDOWPLACEMENT wp;
\r
6759 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6760 wp.length = sizeof(WINDOWPLACEMENT);
\r
6762 wp.showCmd = SW_SHOW;
\r
6763 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6764 wp.rcNormalPosition.left = wpConsole.x;
\r
6765 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6766 wp.rcNormalPosition.top = wpConsole.y;
\r
6767 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6768 SetWindowPlacement(hDlg, &wp);
\r
6771 // [HGM] Chessknight's change 2004-07-13
\r
6772 else { /* Determine Defaults */
\r
6773 WINDOWPLACEMENT wp;
\r
6774 wpConsole.x = wpMain.width + 1;
\r
6775 wpConsole.y = wpMain.y;
\r
6776 wpConsole.width = screenWidth - wpMain.width;
\r
6777 wpConsole.height = wpMain.height;
\r
6778 EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);
\r
6779 wp.length = sizeof(WINDOWPLACEMENT);
\r
6781 wp.showCmd = SW_SHOW;
\r
6782 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
6783 wp.rcNormalPosition.left = wpConsole.x;
\r
6784 wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;
\r
6785 wp.rcNormalPosition.top = wpConsole.y;
\r
6786 wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;
\r
6787 SetWindowPlacement(hDlg, &wp);
\r
6790 // Allow hText to highlight URLs and send notifications on them
\r
6791 wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);
\r
6792 SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
6793 SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);
\r
6794 SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width
\r
6808 if (IsIconic(hDlg)) break;
\r
6809 newSizeX = LOWORD(lParam);
\r
6810 newSizeY = HIWORD(lParam);
\r
6811 if (sizeX != newSizeX || sizeY != newSizeY) {
\r
6812 RECT rectText, rectInput;
\r
6814 int newTextHeight, newTextWidth;
\r
6815 GetWindowRect(hText, &rectText);
\r
6816 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
\r
6817 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
\r
6818 if (newTextHeight < 0) {
\r
6819 newSizeY += -newTextHeight;
\r
6820 newTextHeight = 0;
\r
6822 SetWindowPos(hText, NULL, 0, 0,
\r
6823 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
\r
6824 GetWindowRect(hInput, &rectInput); /* gives screen coords */
\r
6825 pt.x = rectInput.left;
\r
6826 pt.y = rectInput.top + newSizeY - sizeY;
\r
6827 ScreenToClient(hDlg, &pt);
\r
6828 SetWindowPos(hInput, NULL,
\r
6829 pt.x, pt.y, /* needs client coords */
\r
6830 rectInput.right - rectInput.left + newSizeX - sizeX,
\r
6831 rectInput.bottom - rectInput.top, SWP_NOZORDER);
\r
6837 case WM_GETMINMAXINFO:
\r
6838 /* Prevent resizing window too small */
\r
6839 mmi = (MINMAXINFO *) lParam;
\r
6840 mmi->ptMinTrackSize.x = 100;
\r
6841 mmi->ptMinTrackSize.y = 100;
\r
6844 /* [AS] Snapping */
\r
6845 case WM_ENTERSIZEMOVE:
\r
6846 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
6849 return OnSizing( &sd, hDlg, wParam, lParam );
\r
6852 return OnMoving( &sd, hDlg, wParam, lParam );
\r
6854 case WM_EXITSIZEMOVE:
\r
6855 UpdateICSWidth(hText);
\r
6856 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
6859 return DefWindowProc(hDlg, message, wParam, lParam);
\r
6867 if (hwndConsole) return;
\r
6868 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
\r
6869 SendMessage(hCons, WM_INITDIALOG, 0, 0);
\r
6874 ConsoleOutput(char* data, int length, int forceVisible)
\r
6879 char buf[CO_MAX+1];
\r
6882 static int delayLF = 0;
\r
6883 CHARRANGE savesel, sel;
\r
6885 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
\r
6893 while (length--) {
\r
6901 } else if (*p == '\007') {
\r
6902 MyPlaySound(&sounds[(int)SoundBell]);
\r
6909 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
6910 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6911 /* Save current selection */
\r
6912 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
\r
6913 exlen = GetWindowTextLength(hText);
\r
6914 /* Find out whether current end of text is visible */
\r
6915 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
\r
6916 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
\r
6917 /* Trim existing text if it's too long */
\r
6918 if (exlen + (q - buf) > CO_MAX) {
\r
6919 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
\r
6922 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6923 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
\r
6925 savesel.cpMin -= trim;
\r
6926 savesel.cpMax -= trim;
\r
6927 if (exlen < 0) exlen = 0;
\r
6928 if (savesel.cpMin < 0) savesel.cpMin = 0;
\r
6929 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
\r
6931 /* Append the new text */
\r
6932 sel.cpMin = exlen;
\r
6933 sel.cpMax = exlen;
\r
6934 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6935 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
\r
6936 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
\r
6937 if (forceVisible || exlen == 0 ||
\r
6938 (rect.left <= pEnd.x && pEnd.x < rect.right &&
\r
6939 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
\r
6940 /* Scroll to make new end of text visible if old end of text
\r
6941 was visible or new text is an echo of user typein */
\r
6942 sel.cpMin = 9999999;
\r
6943 sel.cpMax = 9999999;
\r
6944 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6945 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6946 SendMessage(hText, EM_SCROLLCARET, 0, 0);
\r
6947 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
\r
6949 if (savesel.cpMax == exlen || forceVisible) {
\r
6950 /* Move insert point to new end of text if it was at the old
\r
6951 end of text or if the new text is an echo of user typein */
\r
6952 sel.cpMin = 9999999;
\r
6953 sel.cpMax = 9999999;
\r
6954 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
6956 /* Restore previous selection */
\r
6957 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
\r
6959 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
\r
6966 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
\r
6970 COLORREF oldFg, oldBg;
\r
6974 if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;
\r
6976 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
6977 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
6978 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
6981 rect.right = x + squareSize;
\r
6983 rect.bottom = y + squareSize;
\r
6986 ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN
\r
6987 + (rightAlign ? (squareSize*2)/3 : 0),
\r
6988 y, ETO_CLIPPED|ETO_OPAQUE,
\r
6989 &rect, str, strlen(str), NULL);
\r
6991 (void) SetTextColor(hdc, oldFg);
\r
6992 (void) SetBkColor(hdc, oldBg);
\r
6993 (void) SelectObject(hdc, oldFont);
\r
6997 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
\r
6998 RECT *rect, char *color, char *flagFell)
\r
7002 COLORREF oldFg, oldBg;
\r
7005 if (appData.clockMode) {
\r
7007 sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);
\r
7009 sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);
\r
7016 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
\r
7017 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
\r
7019 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
\r
7020 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
\r
7022 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
7026 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7027 rect->top, ETO_CLIPPED|ETO_OPAQUE,
\r
7028 rect, str, strlen(str), NULL);
\r
7029 if(logoHeight > 0 && appData.clockMode) {
\r
7031 sprintf(buf, "%s %s", buf+7, flagFell);
\r
7032 r.top = rect->top + logoHeight/2;
\r
7033 r.left = rect->left;
\r
7034 r.right = rect->right;
\r
7035 r.bottom = rect->bottom;
\r
7036 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
\r
7037 r.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7038 &r, str, strlen(str), NULL);
\r
7040 (void) SetTextColor(hdc, oldFg);
\r
7041 (void) SetBkColor(hdc, oldBg);
\r
7042 (void) SelectObject(hdc, oldFont);
\r
7047 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7053 if( count <= 0 ) {
\r
7054 if (appData.debugMode) {
\r
7055 fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );
\r
7058 return ERROR_INVALID_USER_BUFFER;
\r
7061 ResetEvent(ovl->hEvent);
\r
7062 ovl->Offset = ovl->OffsetHigh = 0;
\r
7063 ok = ReadFile(hFile, buf, count, outCount, ovl);
\r
7067 err = GetLastError();
\r
7068 if (err == ERROR_IO_PENDING) {
\r
7069 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7073 err = GetLastError();
\r
7080 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
\r
7085 ResetEvent(ovl->hEvent);
\r
7086 ovl->Offset = ovl->OffsetHigh = 0;
\r
7087 ok = WriteFile(hFile, buf, count, outCount, ovl);
\r
7091 err = GetLastError();
\r
7092 if (err == ERROR_IO_PENDING) {
\r
7093 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
\r
7097 err = GetLastError();
\r
7103 /* [AS] If input is line by line and a line exceed the buffer size, force an error */
\r
7104 void CheckForInputBufferFull( InputSource * is )
\r
7106 if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {
\r
7107 /* Look for end of line */
\r
7108 char * p = is->buf;
\r
7110 while( p < is->next && *p != '\n' ) {
\r
7114 if( p >= is->next ) {
\r
7115 if (appData.debugMode) {
\r
7116 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );
\r
7119 is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */
\r
7120 is->count = (DWORD) -1;
\r
7121 is->next = is->buf;
\r
7127 InputThread(LPVOID arg)
\r
7132 is = (InputSource *) arg;
\r
7133 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
7134 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
7135 while (is->hThread != NULL) {
\r
7136 is->error = DoReadFile(is->hFile, is->next,
\r
7137 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7138 &is->count, &ovl);
\r
7139 if (is->error == NO_ERROR) {
\r
7140 is->next += is->count;
\r
7142 if (is->error == ERROR_BROKEN_PIPE) {
\r
7143 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7146 is->count = (DWORD) -1;
\r
7147 /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */
\r
7152 CheckForInputBufferFull( is );
\r
7154 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7156 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7158 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7161 CloseHandle(ovl.hEvent);
\r
7162 CloseHandle(is->hFile);
\r
7164 if (appData.debugMode) {
\r
7165 fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );
\r
7172 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
\r
7174 NonOvlInputThread(LPVOID arg)
\r
7181 is = (InputSource *) arg;
\r
7182 while (is->hThread != NULL) {
\r
7183 is->error = ReadFile(is->hFile, is->next,
\r
7184 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
\r
7185 &is->count, NULL) ? NO_ERROR : GetLastError();
\r
7186 if (is->error == NO_ERROR) {
\r
7187 /* Change CRLF to LF */
\r
7188 if (is->next > is->buf) {
\r
7190 i = is->count + 1;
\r
7198 if (prev == '\r' && *p == '\n') {
\r
7210 if (is->error == ERROR_BROKEN_PIPE) {
\r
7211 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
\r
7214 is->count = (DWORD) -1;
\r
7218 CheckForInputBufferFull( is );
\r
7220 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7222 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7224 if (is->count < 0) break; /* Quit on error */
\r
7226 CloseHandle(is->hFile);
\r
7231 SocketInputThread(LPVOID arg)
\r
7235 is = (InputSource *) arg;
\r
7236 while (is->hThread != NULL) {
\r
7237 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
\r
7238 if ((int)is->count == SOCKET_ERROR) {
\r
7239 is->count = (DWORD) -1;
\r
7240 is->error = WSAGetLastError();
\r
7242 is->error = NO_ERROR;
\r
7243 is->next += is->count;
\r
7244 if (is->count == 0 && is->second == is) {
\r
7245 /* End of file on stderr; quit with no message */
\r
7249 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
\r
7251 if( is->count == ((DWORD) -1) ) break; /* [AS] */
\r
7253 if (is->count <= 0) break; /* Quit on EOF or error */
\r
7259 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
7263 is = (InputSource *) lParam;
\r
7264 if (is->lineByLine) {
\r
7265 /* Feed in lines one by one */
\r
7266 char *p = is->buf;
\r
7268 while (q < is->next) {
\r
7269 if (*q++ == '\n') {
\r
7270 (is->func)(is, is->closure, p, q - p, NO_ERROR);
\r
7275 /* Move any partial line to the start of the buffer */
\r
7277 while (p < is->next) {
\r
7282 if (is->error != NO_ERROR || is->count == 0) {
\r
7283 /* Notify backend of the error. Note: If there was a partial
\r
7284 line at the end, it is not flushed through. */
\r
7285 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7288 /* Feed in the whole chunk of input at once */
\r
7289 (is->func)(is, is->closure, is->buf, is->count, is->error);
\r
7290 is->next = is->buf;
\r
7294 /*---------------------------------------------------------------------------*\
\r
7296 * Menu enables. Used when setting various modes.
\r
7298 \*---------------------------------------------------------------------------*/
\r
7306 GreyRevert(Boolean grey)
\r
7307 { // [HGM] vari: for retracting variations in local mode
\r
7308 HMENU hmenu = GetMenu(hwndMain);
\r
7309 EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));
\r
7313 SetMenuEnables(HMENU hmenu, Enables *enab)
\r
7315 while (enab->item > 0) {
\r
7316 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
\r
7321 Enables gnuEnables[] = {
\r
7322 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7323 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7324 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7325 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
\r
7326 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
\r
7327 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
\r
7328 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7329 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
\r
7330 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
\r
7331 { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },
\r
7332 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7333 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7337 Enables icsEnables[] = {
\r
7338 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7339 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7340 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7341 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7342 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7343 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7344 { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },
\r
7345 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7346 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7347 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7348 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7349 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7350 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
\r
7351 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7352 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7357 Enables zippyEnables[] = {
\r
7358 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7359 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
\r
7360 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
\r
7361 { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },
\r
7366 Enables ncpEnables[] = {
\r
7367 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
\r
7368 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
\r
7369 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7370 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7371 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7372 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
\r
7373 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
\r
7374 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
\r
7375 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
\r
7376 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
\r
7377 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7378 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7379 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
\r
7380 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
\r
7381 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
\r
7382 { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },
\r
7383 { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },
\r
7384 { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },
\r
7385 { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },
\r
7389 Enables trainingOnEnables[] = {
\r
7390 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
\r
7391 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
\r
7392 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
\r
7393 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
\r
7394 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
\r
7395 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
\r
7396 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
\r
7397 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
\r
7401 Enables trainingOffEnables[] = {
\r
7402 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
\r
7403 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
\r
7404 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
\r
7405 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
\r
7406 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
\r
7407 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
\r
7408 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
\r
7409 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
\r
7413 /* These modify either ncpEnables or gnuEnables */
\r
7414 Enables cmailEnables[] = {
\r
7415 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
\r
7416 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
\r
7417 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
\r
7418 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
\r
7419 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
\r
7420 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
\r
7421 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
\r
7425 Enables machineThinkingEnables[] = {
\r
7426 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7427 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
\r
7428 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
\r
7429 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
\r
7430 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
\r
7431 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7432 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7433 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7434 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
\r
7435 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
\r
7436 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
\r
7437 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
\r
7438 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
\r
7439 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
\r
7440 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
\r
7444 Enables userThinkingEnables[] = {
\r
7445 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7446 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
\r
7447 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
\r
7448 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
\r
7449 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
\r
7450 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7451 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7452 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7453 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
\r
7454 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
\r
7455 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
\r
7456 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
\r
7457 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
\r
7458 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
\r
7459 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
\r
7463 /*---------------------------------------------------------------------------*\
\r
7465 * Front-end interface functions exported by XBoard.
\r
7466 * Functions appear in same order as prototypes in frontend.h.
\r
7468 \*---------------------------------------------------------------------------*/
\r
7472 static UINT prevChecked = 0;
\r
7473 static int prevPausing = 0;
\r
7476 if (pausing != prevPausing) {
\r
7477 prevPausing = pausing;
\r
7478 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
\r
7479 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
\r
7480 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
\r
7483 switch (gameMode) {
\r
7484 case BeginningOfGame:
\r
7485 if (appData.icsActive)
\r
7486 nowChecked = IDM_IcsClient;
\r
7487 else if (appData.noChessProgram)
\r
7488 nowChecked = IDM_EditGame;
\r
7490 nowChecked = IDM_MachineBlack;
\r
7492 case MachinePlaysBlack:
\r
7493 nowChecked = IDM_MachineBlack;
\r
7495 case MachinePlaysWhite:
\r
7496 nowChecked = IDM_MachineWhite;
\r
7498 case TwoMachinesPlay:
\r
7499 nowChecked = IDM_TwoMachines;
\r
7502 nowChecked = IDM_AnalysisMode;
\r
7505 nowChecked = IDM_AnalyzeFile;
\r
7508 nowChecked = IDM_EditGame;
\r
7510 case PlayFromGameFile:
\r
7511 nowChecked = IDM_LoadGame;
\r
7513 case EditPosition:
\r
7514 nowChecked = IDM_EditPosition;
\r
7517 nowChecked = IDM_Training;
\r
7519 case IcsPlayingWhite:
\r
7520 case IcsPlayingBlack:
\r
7521 case IcsObserving:
\r
7523 nowChecked = IDM_IcsClient;
\r
7530 if (prevChecked != 0)
\r
7531 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7532 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
\r
7533 if (nowChecked != 0)
\r
7534 (void) CheckMenuItem(GetMenu(hwndMain),
\r
7535 nowChecked, MF_BYCOMMAND|MF_CHECKED);
\r
7537 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
\r
7538 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
\r
7539 MF_BYCOMMAND|MF_ENABLED);
\r
7541 (void) EnableMenuItem(GetMenu(hwndMain),
\r
7542 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
\r
7545 prevChecked = nowChecked;
\r
7547 /* [DM] icsEngineAnalyze - Do a sceure check too */
\r
7548 if (appData.icsActive) {
\r
7549 if (appData.icsEngineAnalyze) {
\r
7550 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7551 MF_BYCOMMAND|MF_CHECKED);
\r
7553 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7554 MF_BYCOMMAND|MF_UNCHECKED);
\r
7562 HMENU hmenu = GetMenu(hwndMain);
\r
7563 SetMenuEnables(hmenu, icsEnables);
\r
7564 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
\r
7565 MF_BYPOSITION|MF_ENABLED);
\r
7567 if (appData.zippyPlay) {
\r
7568 SetMenuEnables(hmenu, zippyEnables);
\r
7569 if (!appData.noChessProgram) /* [DM] icsEngineAnalyze */
\r
7570 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
\r
7571 MF_BYCOMMAND|MF_ENABLED);
\r
7579 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
\r
7585 HMENU hmenu = GetMenu(hwndMain);
\r
7586 SetMenuEnables(hmenu, ncpEnables);
\r
7587 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
\r
7588 MF_BYPOSITION|MF_GRAYED);
\r
7589 DrawMenuBar(hwndMain);
\r
7595 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
\r
7599 SetTrainingModeOn()
\r
7602 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
\r
7603 for (i = 0; i < N_BUTTONS; i++) {
\r
7604 if (buttonDesc[i].hwnd != NULL)
\r
7605 EnableWindow(buttonDesc[i].hwnd, FALSE);
\r
7610 VOID SetTrainingModeOff()
\r
7613 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
\r
7614 for (i = 0; i < N_BUTTONS; i++) {
\r
7615 if (buttonDesc[i].hwnd != NULL)
\r
7616 EnableWindow(buttonDesc[i].hwnd, TRUE);
\r
7622 SetUserThinkingEnables()
\r
7624 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
\r
7628 SetMachineThinkingEnables()
\r
7630 HMENU hMenu = GetMenu(hwndMain);
\r
7631 int flags = MF_BYCOMMAND|MF_ENABLED;
\r
7633 SetMenuEnables(hMenu, machineThinkingEnables);
\r
7635 if (gameMode == MachinePlaysBlack) {
\r
7636 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
\r
7637 } else if (gameMode == MachinePlaysWhite) {
\r
7638 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
\r
7639 } else if (gameMode == TwoMachinesPlay) {
\r
7640 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
\r
7646 DisplayTitle(char *str)
\r
7648 char title[MSG_SIZ], *host;
\r
7649 if (str[0] != NULLCHAR) {
\r
7650 strcpy(title, str);
\r
7651 } else if (appData.icsActive) {
\r
7652 if (appData.icsCommPort[0] != NULLCHAR)
\r
7655 host = appData.icsHost;
\r
7656 sprintf(title, "%s: %s", szTitle, host);
\r
7657 } else if (appData.noChessProgram) {
\r
7658 strcpy(title, szTitle);
\r
7660 strcpy(title, szTitle);
\r
7661 strcat(title, ": ");
\r
7662 strcat(title, first.tidy);
\r
7664 SetWindowText(hwndMain, title);
\r
7669 DisplayMessage(char *str1, char *str2)
\r
7673 int remain = MESSAGE_TEXT_MAX - 1;
\r
7676 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
\r
7677 messageText[0] = NULLCHAR;
\r
7679 len = strlen(str1);
\r
7680 if (len > remain) len = remain;
\r
7681 strncpy(messageText, str1, len);
\r
7682 messageText[len] = NULLCHAR;
\r
7685 if (*str2 && remain >= 2) {
\r
7687 strcat(messageText, " ");
\r
7690 len = strlen(str2);
\r
7691 if (len > remain) len = remain;
\r
7692 strncat(messageText, str2, len);
\r
7694 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
\r
7696 if (hwndMain == NULL || IsIconic(hwndMain)) return;
\r
7700 hdc = GetDC(hwndMain);
\r
7701 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
7702 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
\r
7703 &messageRect, messageText, strlen(messageText), NULL);
\r
7704 (void) SelectObject(hdc, oldFont);
\r
7705 (void) ReleaseDC(hwndMain, hdc);
\r
7709 DisplayError(char *str, int error)
\r
7711 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
\r
7717 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7718 NULL, error, LANG_NEUTRAL,
\r
7719 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7721 sprintf(buf, "%s:\n%s", str, buf2);
\r
7723 ErrorMap *em = errmap;
\r
7724 while (em->err != 0 && em->err != error) em++;
\r
7725 if (em->err != 0) {
\r
7726 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7728 sprintf(buf, "%s:\nError code %d", str, error);
\r
7733 ErrorPopUp("Error", buf);
\r
7738 DisplayMoveError(char *str)
\r
7740 fromX = fromY = -1;
\r
7741 ClearHighlights();
\r
7742 DrawPosition(FALSE, NULL);
\r
7743 if (appData.popupMoveErrors) {
\r
7744 ErrorPopUp("Error", str);
\r
7746 DisplayMessage(str, "");
\r
7747 moveErrorMessageUp = TRUE;
\r
7752 DisplayFatalError(char *str, int error, int exitStatus)
\r
7754 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
\r
7756 char *label = exitStatus ? "Fatal Error" : "Exiting";
\r
7759 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
\r
7760 NULL, error, LANG_NEUTRAL,
\r
7761 (LPSTR) buf2, MSG_SIZ, NULL);
\r
7763 sprintf(buf, "%s:\n%s", str, buf2);
\r
7765 ErrorMap *em = errmap;
\r
7766 while (em->err != 0 && em->err != error) em++;
\r
7767 if (em->err != 0) {
\r
7768 sprintf(buf, "%s:\n%s", str, em->msg);
\r
7770 sprintf(buf, "%s:\nError code %d", str, error);
\r
7775 if (appData.debugMode) {
\r
7776 fprintf(debugFP, "%s: %s\n", label, str);
\r
7778 if (appData.popupExitMessage) {
\r
7779 (void) MessageBox(hwndMain, str, label, MB_OK|
\r
7780 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
\r
7782 ExitEvent(exitStatus);
\r
7787 DisplayInformation(char *str)
\r
7789 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
\r
7794 DisplayNote(char *str)
\r
7796 ErrorPopUp("Note", str);
\r
7801 char *title, *question, *replyPrefix;
\r
7806 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7808 static QuestionParams *qp;
\r
7809 char reply[MSG_SIZ];
\r
7812 switch (message) {
\r
7813 case WM_INITDIALOG:
\r
7814 qp = (QuestionParams *) lParam;
\r
7815 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7816 SetWindowText(hDlg, qp->title);
\r
7817 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
\r
7818 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
\r
7822 switch (LOWORD(wParam)) {
\r
7824 strcpy(reply, qp->replyPrefix);
\r
7825 if (*reply) strcat(reply, " ");
\r
7826 len = strlen(reply);
\r
7827 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
\r
7828 strcat(reply, "\n");
\r
7829 OutputToProcess(qp->pr, reply, strlen(reply), &err);
\r
7830 EndDialog(hDlg, TRUE);
\r
7831 if (err) DisplayFatalError("Error writing to chess program", err, 1);
\r
7834 EndDialog(hDlg, FALSE);
\r
7845 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
\r
7847 QuestionParams qp;
\r
7851 qp.question = question;
\r
7852 qp.replyPrefix = replyPrefix;
\r
7854 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
\r
7855 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
\r
7856 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
\r
7857 FreeProcInstance(lpProc);
\r
7860 /* [AS] Pick FRC position */
\r
7861 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7863 static int * lpIndexFRC;
\r
7869 case WM_INITDIALOG:
\r
7870 lpIndexFRC = (int *) lParam;
\r
7872 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7874 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );
\r
7875 SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );
\r
7876 SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );
\r
7877 SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));
\r
7882 switch( LOWORD(wParam) ) {
\r
7884 *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7885 EndDialog( hDlg, 0 );
\r
7886 shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */
\r
7889 EndDialog( hDlg, 1 );
\r
7891 case IDC_NFG_Edit:
\r
7892 if( HIWORD(wParam) == EN_CHANGE ) {
\r
7893 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );
\r
7895 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );
\r
7898 case IDC_NFG_Random:
\r
7899 sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */
\r
7900 SetDlgItemText(hDlg, IDC_NFG_Edit, buf );
\r
7913 int index = appData.defaultFrcPosition;
\r
7914 FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );
\r
7916 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );
\r
7918 if( result == 0 ) {
\r
7919 appData.defaultFrcPosition = index;
\r
7925 /* [AS] Game list options. Refactored by HGM */
\r
7927 HWND gameListOptionsDialog;
\r
7929 // low-level front-end: clear text edit / list widget
\r
7933 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );
\r
7936 // low-level front-end: clear text edit / list widget
\r
7938 GLT_DeSelectList()
\r
7940 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );
\r
7943 // low-level front-end: append line to text edit / list widget
\r
7945 GLT_AddToList( char *name )
\r
7948 SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );
\r
7952 // low-level front-end: get line from text edit / list widget
\r
7954 GLT_GetFromList( int index, char *name )
\r
7957 if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )
\r
7963 void GLT_MoveSelection( HWND hDlg, int delta )
\r
7965 int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );
\r
7966 int idx2 = idx1 + delta;
\r
7967 int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );
\r
7969 if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {
\r
7972 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );
\r
7973 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );
\r
7974 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );
\r
7975 SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );
\r
7979 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
7983 case WM_INITDIALOG:
\r
7984 gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end
\r
7986 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
7988 /* Initialize list */
\r
7989 GLT_TagsToList( lpUserGLT );
\r
7991 SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );
\r
7996 switch( LOWORD(wParam) ) {
\r
7999 EndDialog( hDlg, 0 );
\r
8002 EndDialog( hDlg, 1 );
\r
8005 case IDC_GLT_Default:
\r
8006 GLT_TagsToList( GLT_DEFAULT_TAGS );
\r
8009 case IDC_GLT_Restore:
\r
8010 GLT_TagsToList( appData.gameListTags );
\r
8014 GLT_MoveSelection( hDlg, -1 );
\r
8017 case IDC_GLT_Down:
\r
8018 GLT_MoveSelection( hDlg, +1 );
\r
8028 int GameListOptions()
\r
8031 FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );
\r
8033 strcpy( lpUserGLT, appData.gameListTags );
\r
8035 result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );
\r
8037 if( result == 0 ) {
\r
8038 /* [AS] Memory leak here! */
\r
8039 appData.gameListTags = strdup( lpUserGLT );
\r
8046 DisplayIcsInteractionTitle(char *str)
\r
8048 char consoleTitle[MSG_SIZ];
\r
8050 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
\r
8051 SetWindowText(hwndConsole, consoleTitle);
\r
8055 DrawPosition(int fullRedraw, Board board)
\r
8057 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
\r
8060 void NotifyFrontendLogin()
\r
8063 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));
\r
8069 fromX = fromY = -1;
\r
8070 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
\r
8071 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8072 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
8073 dragInfo.lastpos = dragInfo.pos;
\r
8074 dragInfo.start.x = dragInfo.start.y = -1;
\r
8075 dragInfo.from = dragInfo.start;
\r
8077 DrawPosition(TRUE, NULL);
\r
8083 CommentPopUp(char *title, char *str)
\r
8085 HWND hwnd = GetActiveWindow();
\r
8086 EitherCommentPopUp(0, title, str, FALSE);
\r
8088 SetActiveWindow(hwnd);
\r
8092 CommentPopDown(void)
\r
8094 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
\r
8095 if (commentDialog) {
\r
8096 ShowWindow(commentDialog, SW_HIDE);
\r
8098 commentUp = FALSE;
\r
8102 EditCommentPopUp(int index, char *title, char *str)
\r
8104 EitherCommentPopUp(index, title, str, TRUE);
\r
8111 MyPlaySound(&sounds[(int)SoundMove]);
\r
8114 VOID PlayIcsWinSound()
\r
8116 MyPlaySound(&sounds[(int)SoundIcsWin]);
\r
8119 VOID PlayIcsLossSound()
\r
8121 MyPlaySound(&sounds[(int)SoundIcsLoss]);
\r
8124 VOID PlayIcsDrawSound()
\r
8126 MyPlaySound(&sounds[(int)SoundIcsDraw]);
\r
8129 VOID PlayIcsUnfinishedSound()
\r
8131 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
\r
8137 MyPlaySound(&sounds[(int)SoundAlarm]);
\r
8145 consoleEcho = TRUE;
\r
8146 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8147 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
\r
8148 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
\r
8157 consoleEcho = FALSE;
\r
8158 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
8159 /* This works OK: set text and background both to the same color */
\r
8161 cf.crTextColor = COLOR_ECHOOFF;
\r
8162 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
\r
8163 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
\r
8166 /* No Raw()...? */
\r
8168 void Colorize(ColorClass cc, int continuation)
\r
8170 currentColorClass = cc;
\r
8171 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
\r
8172 consoleCF.crTextColor = textAttribs[cc].color;
\r
8173 consoleCF.dwEffects = textAttribs[cc].effects;
\r
8174 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
\r
8180 static char buf[MSG_SIZ];
\r
8181 DWORD bufsiz = MSG_SIZ;
\r
8183 if(appData.userName != NULL && appData.userName[0] != 0) {
\r
8184 return appData.userName; /* [HGM] username: prefer name selected by user over his system login */
\r
8186 if (!GetUserName(buf, &bufsiz)) {
\r
8187 /*DisplayError("Error getting user name", GetLastError());*/
\r
8188 strcpy(buf, "User");
\r
8196 static char buf[MSG_SIZ];
\r
8197 DWORD bufsiz = MSG_SIZ;
\r
8199 if (!GetComputerName(buf, &bufsiz)) {
\r
8200 /*DisplayError("Error getting host name", GetLastError());*/
\r
8201 strcpy(buf, "Unknown");
\r
8208 ClockTimerRunning()
\r
8210 return clockTimerEvent != 0;
\r
8216 if (clockTimerEvent == 0) return FALSE;
\r
8217 KillTimer(hwndMain, clockTimerEvent);
\r
8218 clockTimerEvent = 0;
\r
8223 StartClockTimer(long millisec)
\r
8225 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
\r
8226 (UINT) millisec, NULL);
\r
8230 DisplayWhiteClock(long timeRemaining, int highlight)
\r
8233 char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8235 if(appData.noGUI) return;
\r
8236 hdc = GetDC(hwndMain);
\r
8237 if (!IsIconic(hwndMain)) {
\r
8238 DisplayAClock(hdc, timeRemaining, highlight,
\r
8239 flipClock ? &blackRect : &whiteRect, "White", flag);
\r
8241 if (highlight && iconCurrent == iconBlack) {
\r
8242 iconCurrent = iconWhite;
\r
8243 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8244 if (IsIconic(hwndMain)) {
\r
8245 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8248 (void) ReleaseDC(hwndMain, hdc);
\r
8250 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8254 DisplayBlackClock(long timeRemaining, int highlight)
\r
8257 char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";
\r
8259 if(appData.noGUI) return;
\r
8260 hdc = GetDC(hwndMain);
\r
8261 if (!IsIconic(hwndMain)) {
\r
8262 DisplayAClock(hdc, timeRemaining, highlight,
\r
8263 flipClock ? &whiteRect : &blackRect, "Black", flag);
\r
8265 if (highlight && iconCurrent == iconWhite) {
\r
8266 iconCurrent = iconBlack;
\r
8267 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8268 if (IsIconic(hwndMain)) {
\r
8269 DrawIcon(hdc, 2, 2, iconCurrent);
\r
8272 (void) ReleaseDC(hwndMain, hdc);
\r
8274 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
\r
8279 LoadGameTimerRunning()
\r
8281 return loadGameTimerEvent != 0;
\r
8285 StopLoadGameTimer()
\r
8287 if (loadGameTimerEvent == 0) return FALSE;
\r
8288 KillTimer(hwndMain, loadGameTimerEvent);
\r
8289 loadGameTimerEvent = 0;
\r
8294 StartLoadGameTimer(long millisec)
\r
8296 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
\r
8297 (UINT) millisec, NULL);
\r
8305 char fileTitle[MSG_SIZ];
\r
8307 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
8308 f = OpenFileDialog(hwndMain, "a", defName,
\r
8309 appData.oldSaveStyle ? "gam" : "pgn",
\r
8311 "Save Game to File", NULL, fileTitle, NULL);
\r
8313 SaveGame(f, 0, "");
\r
8320 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
\r
8322 if (delayedTimerEvent != 0) {
\r
8323 if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug
\r
8324 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
\r
8326 KillTimer(hwndMain, delayedTimerEvent);
\r
8327 delayedTimerEvent = 0;
\r
8328 if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it
\r
8329 delayedTimerCallback();
\r
8331 delayedTimerCallback = cb;
\r
8332 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
\r
8333 (UINT) millisec, NULL);
\r
8336 DelayedEventCallback
\r
8339 if (delayedTimerEvent) {
\r
8340 return delayedTimerCallback;
\r
8347 CancelDelayedEvent()
\r
8349 if (delayedTimerEvent) {
\r
8350 KillTimer(hwndMain, delayedTimerEvent);
\r
8351 delayedTimerEvent = 0;
\r
8355 DWORD GetWin32Priority(int nice)
\r
8356 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)
\r
8358 REALTIME_PRIORITY_CLASS 0x00000100
\r
8359 HIGH_PRIORITY_CLASS 0x00000080
\r
8360 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
\r
8361 NORMAL_PRIORITY_CLASS 0x00000020
\r
8362 BELOW_NORMAL_PRIORITY_CLASS 0x00004000
\r
8363 IDLE_PRIORITY_CLASS 0x00000040
\r
8365 if (nice < -15) return 0x00000080;
\r
8366 if (nice < 0) return 0x00008000;
\r
8367 if (nice == 0) return 0x00000020;
\r
8368 if (nice < 15) return 0x00004000;
\r
8369 return 0x00000040;
\r
8372 /* Start a child process running the given program.
\r
8373 The process's standard output can be read from "from", and its
\r
8374 standard input can be written to "to".
\r
8375 Exit with fatal error if anything goes wrong.
\r
8376 Returns an opaque pointer that can be used to destroy the process
\r
8380 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
\r
8382 #define BUFSIZE 4096
\r
8384 HANDLE hChildStdinRd, hChildStdinWr,
\r
8385 hChildStdoutRd, hChildStdoutWr;
\r
8386 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
\r
8387 SECURITY_ATTRIBUTES saAttr;
\r
8389 PROCESS_INFORMATION piProcInfo;
\r
8390 STARTUPINFO siStartInfo;
\r
8392 char buf[MSG_SIZ];
\r
8395 if (appData.debugMode) {
\r
8396 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
\r
8401 /* Set the bInheritHandle flag so pipe handles are inherited. */
\r
8402 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
8403 saAttr.bInheritHandle = TRUE;
\r
8404 saAttr.lpSecurityDescriptor = NULL;
\r
8407 * The steps for redirecting child's STDOUT:
\r
8408 * 1. Create anonymous pipe to be STDOUT for child.
\r
8409 * 2. Create a noninheritable duplicate of read handle,
\r
8410 * and close the inheritable read handle.
\r
8413 /* Create a pipe for the child's STDOUT. */
\r
8414 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
\r
8415 return GetLastError();
\r
8418 /* Duplicate the read handle to the pipe, so it is not inherited. */
\r
8419 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
\r
8420 GetCurrentProcess(), &hChildStdoutRdDup, 0,
\r
8421 FALSE, /* not inherited */
\r
8422 DUPLICATE_SAME_ACCESS);
\r
8424 return GetLastError();
\r
8426 CloseHandle(hChildStdoutRd);
\r
8429 * The steps for redirecting child's STDIN:
\r
8430 * 1. Create anonymous pipe to be STDIN for child.
\r
8431 * 2. Create a noninheritable duplicate of write handle,
\r
8432 * and close the inheritable write handle.
\r
8435 /* Create a pipe for the child's STDIN. */
\r
8436 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
\r
8437 return GetLastError();
\r
8440 /* Duplicate the write handle to the pipe, so it is not inherited. */
\r
8441 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
\r
8442 GetCurrentProcess(), &hChildStdinWrDup, 0,
\r
8443 FALSE, /* not inherited */
\r
8444 DUPLICATE_SAME_ACCESS);
\r
8446 return GetLastError();
\r
8448 CloseHandle(hChildStdinWr);
\r
8450 /* Arrange to (1) look in dir for the child .exe file, and
\r
8451 * (2) have dir be the child's working directory. Interpret
\r
8452 * dir relative to the directory WinBoard loaded from. */
\r
8453 GetCurrentDirectory(MSG_SIZ, buf);
\r
8454 SetCurrentDirectory(installDir);
\r
8455 SetCurrentDirectory(dir);
\r
8457 /* Now create the child process. */
\r
8459 siStartInfo.cb = sizeof(STARTUPINFO);
\r
8460 siStartInfo.lpReserved = NULL;
\r
8461 siStartInfo.lpDesktop = NULL;
\r
8462 siStartInfo.lpTitle = NULL;
\r
8463 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
\r
8464 siStartInfo.cbReserved2 = 0;
\r
8465 siStartInfo.lpReserved2 = NULL;
\r
8466 siStartInfo.hStdInput = hChildStdinRd;
\r
8467 siStartInfo.hStdOutput = hChildStdoutWr;
\r
8468 siStartInfo.hStdError = hChildStdoutWr;
\r
8470 fSuccess = CreateProcess(NULL,
\r
8471 cmdLine, /* command line */
\r
8472 NULL, /* process security attributes */
\r
8473 NULL, /* primary thread security attrs */
\r
8474 TRUE, /* handles are inherited */
\r
8475 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
\r
8476 NULL, /* use parent's environment */
\r
8478 &siStartInfo, /* STARTUPINFO pointer */
\r
8479 &piProcInfo); /* receives PROCESS_INFORMATION */
\r
8481 err = GetLastError();
\r
8482 SetCurrentDirectory(buf); /* return to prev directory */
\r
8487 if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority
\r
8488 if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);
\r
8489 SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));
\r
8492 /* Close the handles we don't need in the parent */
\r
8493 CloseHandle(piProcInfo.hThread);
\r
8494 CloseHandle(hChildStdinRd);
\r
8495 CloseHandle(hChildStdoutWr);
\r
8497 /* Prepare return value */
\r
8498 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8499 cp->kind = CPReal;
\r
8500 cp->hProcess = piProcInfo.hProcess;
\r
8501 cp->pid = piProcInfo.dwProcessId;
\r
8502 cp->hFrom = hChildStdoutRdDup;
\r
8503 cp->hTo = hChildStdinWrDup;
\r
8505 *pr = (void *) cp;
\r
8507 /* Klaus Friedel says that this Sleep solves a problem under Windows
\r
8508 2000 where engines sometimes don't see the initial command(s)
\r
8509 from WinBoard and hang. I don't understand how that can happen,
\r
8510 but the Sleep is harmless, so I've put it in. Others have also
\r
8511 reported what may be the same problem, so hopefully this will fix
\r
8512 it for them too. */
\r
8520 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
\r
8522 ChildProc *cp; int result;
\r
8524 cp = (ChildProc *) pr;
\r
8525 if (cp == NULL) return;
\r
8527 switch (cp->kind) {
\r
8529 /* TerminateProcess is considered harmful, so... */
\r
8530 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
\r
8531 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
\r
8532 /* The following doesn't work because the chess program
\r
8533 doesn't "have the same console" as WinBoard. Maybe
\r
8534 we could arrange for this even though neither WinBoard
\r
8535 nor the chess program uses a console for stdio? */
\r
8536 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
\r
8538 /* [AS] Special termination modes for misbehaving programs... */
\r
8539 if( signal == 9 ) {
\r
8540 result = TerminateProcess( cp->hProcess, 0 );
\r
8542 if ( appData.debugMode) {
\r
8543 fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );
\r
8546 else if( signal == 10 ) {
\r
8547 DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most
\r
8549 if( dw != WAIT_OBJECT_0 ) {
\r
8550 result = TerminateProcess( cp->hProcess, 0 );
\r
8552 if ( appData.debugMode) {
\r
8553 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );
\r
8559 CloseHandle(cp->hProcess);
\r
8563 if (cp->hFrom) CloseHandle(cp->hFrom);
\r
8567 closesocket(cp->sock);
\r
8572 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
\r
8573 closesocket(cp->sock);
\r
8574 closesocket(cp->sock2);
\r
8582 InterruptChildProcess(ProcRef pr)
\r
8586 cp = (ChildProc *) pr;
\r
8587 if (cp == NULL) return;
\r
8588 switch (cp->kind) {
\r
8590 /* The following doesn't work because the chess program
\r
8591 doesn't "have the same console" as WinBoard. Maybe
\r
8592 we could arrange for this even though neither WinBoard
\r
8593 nor the chess program uses a console for stdio */
\r
8594 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
\r
8599 /* Can't interrupt */
\r
8603 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
\r
8610 OpenTelnet(char *host, char *port, ProcRef *pr)
\r
8612 char cmdLine[MSG_SIZ];
\r
8614 if (port[0] == NULLCHAR) {
\r
8615 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
\r
8617 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
\r
8619 return StartChildProcess(cmdLine, "", pr);
\r
8623 /* Code to open TCP sockets */
\r
8626 OpenTCP(char *host, char *port, ProcRef *pr)
\r
8631 struct sockaddr_in sa, mysa;
\r
8632 struct hostent FAR *hp;
\r
8633 unsigned short uport;
\r
8634 WORD wVersionRequested;
\r
8637 /* Initialize socket DLL */
\r
8638 wVersionRequested = MAKEWORD(1, 1);
\r
8639 err = WSAStartup(wVersionRequested, &wsaData);
\r
8640 if (err != 0) return err;
\r
8643 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8644 err = WSAGetLastError();
\r
8649 /* Bind local address using (mostly) don't-care values.
\r
8651 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8652 mysa.sin_family = AF_INET;
\r
8653 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8654 uport = (unsigned short) 0;
\r
8655 mysa.sin_port = htons(uport);
\r
8656 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8657 == SOCKET_ERROR) {
\r
8658 err = WSAGetLastError();
\r
8663 /* Resolve remote host name */
\r
8664 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8665 if (!(hp = gethostbyname(host))) {
\r
8666 unsigned int b0, b1, b2, b3;
\r
8668 err = WSAGetLastError();
\r
8670 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8671 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8672 hp->h_addrtype = AF_INET;
\r
8674 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8675 hp->h_addr_list[0] = (char *) malloc(4);
\r
8676 hp->h_addr_list[0][0] = (char) b0;
\r
8677 hp->h_addr_list[0][1] = (char) b1;
\r
8678 hp->h_addr_list[0][2] = (char) b2;
\r
8679 hp->h_addr_list[0][3] = (char) b3;
\r
8685 sa.sin_family = hp->h_addrtype;
\r
8686 uport = (unsigned short) atoi(port);
\r
8687 sa.sin_port = htons(uport);
\r
8688 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8690 /* Make connection */
\r
8691 if (connect(s, (struct sockaddr *) &sa,
\r
8692 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8693 err = WSAGetLastError();
\r
8698 /* Prepare return value */
\r
8699 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8700 cp->kind = CPSock;
\r
8702 *pr = (ProcRef *) cp;
\r
8708 OpenCommPort(char *name, ProcRef *pr)
\r
8713 char fullname[MSG_SIZ];
\r
8715 if (*name != '\\')
\r
8716 sprintf(fullname, "\\\\.\\%s", name);
\r
8718 strcpy(fullname, name);
\r
8720 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
\r
8721 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
\r
8722 if (h == (HANDLE) -1) {
\r
8723 return GetLastError();
\r
8727 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
\r
8729 /* Accumulate characters until a 100ms pause, then parse */
\r
8730 ct.ReadIntervalTimeout = 100;
\r
8731 ct.ReadTotalTimeoutMultiplier = 0;
\r
8732 ct.ReadTotalTimeoutConstant = 0;
\r
8733 ct.WriteTotalTimeoutMultiplier = 0;
\r
8734 ct.WriteTotalTimeoutConstant = 0;
\r
8735 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
\r
8737 /* Prepare return value */
\r
8738 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8739 cp->kind = CPComm;
\r
8742 *pr = (ProcRef *) cp;
\r
8748 OpenLoopback(ProcRef *pr)
\r
8750 DisplayFatalError("Not implemented", 0, 1);
\r
8756 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
\r
8761 struct sockaddr_in sa, mysa;
\r
8762 struct hostent FAR *hp;
\r
8763 unsigned short uport;
\r
8764 WORD wVersionRequested;
\r
8767 char stderrPortStr[MSG_SIZ];
\r
8769 /* Initialize socket DLL */
\r
8770 wVersionRequested = MAKEWORD(1, 1);
\r
8771 err = WSAStartup(wVersionRequested, &wsaData);
\r
8772 if (err != 0) return err;
\r
8774 /* Resolve remote host name */
\r
8775 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
\r
8776 if (!(hp = gethostbyname(host))) {
\r
8777 unsigned int b0, b1, b2, b3;
\r
8779 err = WSAGetLastError();
\r
8781 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
\r
8782 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
\r
8783 hp->h_addrtype = AF_INET;
\r
8785 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
\r
8786 hp->h_addr_list[0] = (char *) malloc(4);
\r
8787 hp->h_addr_list[0][0] = (char) b0;
\r
8788 hp->h_addr_list[0][1] = (char) b1;
\r
8789 hp->h_addr_list[0][2] = (char) b2;
\r
8790 hp->h_addr_list[0][3] = (char) b3;
\r
8796 sa.sin_family = hp->h_addrtype;
\r
8797 uport = (unsigned short) 514;
\r
8798 sa.sin_port = htons(uport);
\r
8799 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
\r
8801 /* Bind local socket to unused "privileged" port address
\r
8803 s = INVALID_SOCKET;
\r
8804 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8805 mysa.sin_family = AF_INET;
\r
8806 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8807 for (fromPort = 1023;; fromPort--) {
\r
8808 if (fromPort < 0) {
\r
8810 return WSAEADDRINUSE;
\r
8812 if (s == INVALID_SOCKET) {
\r
8813 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8814 err = WSAGetLastError();
\r
8819 uport = (unsigned short) fromPort;
\r
8820 mysa.sin_port = htons(uport);
\r
8821 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8822 == SOCKET_ERROR) {
\r
8823 err = WSAGetLastError();
\r
8824 if (err == WSAEADDRINUSE) continue;
\r
8828 if (connect(s, (struct sockaddr *) &sa,
\r
8829 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
\r
8830 err = WSAGetLastError();
\r
8831 if (err == WSAEADDRINUSE) {
\r
8842 /* Bind stderr local socket to unused "privileged" port address
\r
8844 s2 = INVALID_SOCKET;
\r
8845 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
\r
8846 mysa.sin_family = AF_INET;
\r
8847 mysa.sin_addr.s_addr = INADDR_ANY;
\r
8848 for (fromPort = 1023;; fromPort--) {
\r
8849 if (fromPort == prevStderrPort) continue; // don't reuse port
\r
8850 if (fromPort < 0) {
\r
8851 (void) closesocket(s);
\r
8853 return WSAEADDRINUSE;
\r
8855 if (s2 == INVALID_SOCKET) {
\r
8856 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
\r
8857 err = WSAGetLastError();
\r
8863 uport = (unsigned short) fromPort;
\r
8864 mysa.sin_port = htons(uport);
\r
8865 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
\r
8866 == SOCKET_ERROR) {
\r
8867 err = WSAGetLastError();
\r
8868 if (err == WSAEADDRINUSE) continue;
\r
8869 (void) closesocket(s);
\r
8873 if (listen(s2, 1) == SOCKET_ERROR) {
\r
8874 err = WSAGetLastError();
\r
8875 if (err == WSAEADDRINUSE) {
\r
8877 s2 = INVALID_SOCKET;
\r
8880 (void) closesocket(s);
\r
8881 (void) closesocket(s2);
\r
8887 prevStderrPort = fromPort; // remember port used
\r
8888 sprintf(stderrPortStr, "%d", fromPort);
\r
8890 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
\r
8891 err = WSAGetLastError();
\r
8892 (void) closesocket(s);
\r
8893 (void) closesocket(s2);
\r
8898 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
\r
8899 err = WSAGetLastError();
\r
8900 (void) closesocket(s);
\r
8901 (void) closesocket(s2);
\r
8905 if (*user == NULLCHAR) user = UserName();
\r
8906 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
\r
8907 err = WSAGetLastError();
\r
8908 (void) closesocket(s);
\r
8909 (void) closesocket(s2);
\r
8913 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
\r
8914 err = WSAGetLastError();
\r
8915 (void) closesocket(s);
\r
8916 (void) closesocket(s2);
\r
8921 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
\r
8922 err = WSAGetLastError();
\r
8923 (void) closesocket(s);
\r
8924 (void) closesocket(s2);
\r
8928 (void) closesocket(s2); /* Stop listening */
\r
8930 /* Prepare return value */
\r
8931 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
\r
8932 cp->kind = CPRcmd;
\r
8935 *pr = (ProcRef *) cp;
\r
8942 AddInputSource(ProcRef pr, int lineByLine,
\r
8943 InputCallback func, VOIDSTAR closure)
\r
8945 InputSource *is, *is2 = NULL;
\r
8946 ChildProc *cp = (ChildProc *) pr;
\r
8948 is = (InputSource *) calloc(1, sizeof(InputSource));
\r
8949 is->lineByLine = lineByLine;
\r
8951 is->closure = closure;
\r
8952 is->second = NULL;
\r
8953 is->next = is->buf;
\r
8954 if (pr == NoProc) {
\r
8955 is->kind = CPReal;
\r
8956 consoleInputSource = is;
\r
8958 is->kind = cp->kind;
\r
8960 [AS] Try to avoid a race condition if the thread is given control too early:
\r
8961 we create all threads suspended so that the is->hThread variable can be
\r
8962 safely assigned, then let the threads start with ResumeThread.
\r
8964 switch (cp->kind) {
\r
8966 is->hFile = cp->hFrom;
\r
8967 cp->hFrom = NULL; /* now owned by InputThread */
\r
8969 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
\r
8970 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8974 is->hFile = cp->hFrom;
\r
8975 cp->hFrom = NULL; /* now owned by InputThread */
\r
8977 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
\r
8978 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8982 is->sock = cp->sock;
\r
8984 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8985 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8989 is2 = (InputSource *) calloc(1, sizeof(InputSource));
\r
8991 is->sock = cp->sock;
\r
8993 is2->sock = cp->sock2;
\r
8994 is2->second = is2;
\r
8996 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
8997 (LPVOID) is, CREATE_SUSPENDED, &is->id);
\r
8999 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
\r
9000 (LPVOID) is2, CREATE_SUSPENDED, &is2->id);
\r
9004 if( is->hThread != NULL ) {
\r
9005 ResumeThread( is->hThread );
\r
9008 if( is2 != NULL && is2->hThread != NULL ) {
\r
9009 ResumeThread( is2->hThread );
\r
9013 return (InputSourceRef) is;
\r
9017 RemoveInputSource(InputSourceRef isr)
\r
9021 is = (InputSource *) isr;
\r
9022 is->hThread = NULL; /* tell thread to stop */
\r
9023 CloseHandle(is->hThread);
\r
9024 if (is->second != NULL) {
\r
9025 is->second->hThread = NULL;
\r
9026 CloseHandle(is->second->hThread);
\r
9030 int no_wrap(char *message, int count)
\r
9032 ConsoleOutput(message, count, FALSE);
\r
9037 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
\r
9040 int outCount = SOCKET_ERROR;
\r
9041 ChildProc *cp = (ChildProc *) pr;
\r
9042 static OVERLAPPED ovl;
\r
9043 static int line = 0;
\r
9047 if (appData.noJoin || !appData.useInternalWrap)
\r
9048 return no_wrap(message, count);
\r
9051 int width = get_term_width();
\r
9052 int len = wrap(NULL, message, count, width, &line);
\r
9053 char *msg = malloc(len);
\r
9057 return no_wrap(message, count);
\r
9060 dbgchk = wrap(msg, message, count, width, &line);
\r
9061 if (dbgchk != len && appData.debugMode)
\r
9062 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
\r
9063 ConsoleOutput(msg, len, FALSE);
\r
9070 if (ovl.hEvent == NULL) {
\r
9071 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
\r
9073 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
\r
9075 switch (cp->kind) {
\r
9078 outCount = send(cp->sock, message, count, 0);
\r
9079 if (outCount == SOCKET_ERROR) {
\r
9080 *outError = WSAGetLastError();
\r
9082 *outError = NO_ERROR;
\r
9087 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9088 &dOutCount, NULL)) {
\r
9089 *outError = NO_ERROR;
\r
9090 outCount = (int) dOutCount;
\r
9092 *outError = GetLastError();
\r
9097 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
\r
9098 &dOutCount, &ovl);
\r
9099 if (*outError == NO_ERROR) {
\r
9100 outCount = (int) dOutCount;
\r
9108 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
\r
9111 /* Ignore delay, not implemented for WinBoard */
\r
9112 return OutputToProcess(pr, message, count, outError);
\r
9117 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
\r
9118 char *buf, int count, int error)
\r
9120 DisplayFatalError("Not implemented", 0, 1);
\r
9123 /* see wgamelist.c for Game List functions */
\r
9124 /* see wedittags.c for Edit Tags functions */
\r
9131 char buf[MSG_SIZ];
\r
9134 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
\r
9135 f = fopen(buf, "r");
\r
9137 ProcessICSInitScript(f);
\r
9145 StartAnalysisClock()
\r
9147 if (analysisTimerEvent) return;
\r
9148 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
\r
9149 (UINT) 2000, NULL);
\r
9153 SetHighlights(int fromX, int fromY, int toX, int toY)
\r
9155 highlightInfo.sq[0].x = fromX;
\r
9156 highlightInfo.sq[0].y = fromY;
\r
9157 highlightInfo.sq[1].x = toX;
\r
9158 highlightInfo.sq[1].y = toY;
\r
9164 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
\r
9165 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
\r
9169 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
\r
9171 premoveHighlightInfo.sq[0].x = fromX;
\r
9172 premoveHighlightInfo.sq[0].y = fromY;
\r
9173 premoveHighlightInfo.sq[1].x = toX;
\r
9174 premoveHighlightInfo.sq[1].y = toY;
\r
9178 ClearPremoveHighlights()
\r
9180 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
\r
9181 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
\r
9185 ShutDownFrontEnd()
\r
9187 if (saveSettingsOnExit) SaveSettings(settingsFileName);
\r
9188 DeleteClipboardTempFiles();
\r
9194 if (IsIconic(hwndMain))
\r
9195 ShowWindow(hwndMain, SW_RESTORE);
\r
9197 SetActiveWindow(hwndMain);
\r
9201 * Prototypes for animation support routines
\r
9203 static void ScreenSquare(int column, int row, POINT * pt);
\r
9204 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
\r
9205 POINT frames[], int * nFrames);
\r
9209 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)
\r
9210 { // [HGM] atomic: animate blast wave
\r
9212 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);
\r
9213 explodeInfo.fromX = fromX;
\r
9214 explodeInfo.fromY = fromY;
\r
9215 explodeInfo.toX = toX;
\r
9216 explodeInfo.toY = toY;
\r
9217 for(i=1; i<nFrames; i++) {
\r
9218 explodeInfo.radius = (i*180)/(nFrames-1);
\r
9219 DrawPosition(FALSE, NULL);
\r
9220 Sleep(appData.animSpeed);
\r
9222 explodeInfo.radius = 0;
\r
9223 DrawPosition(TRUE, NULL);
\r
9229 AnimateMove(board, fromX, fromY, toX, toY)
\r
9236 ChessSquare piece;
\r
9237 POINT start, finish, mid;
\r
9238 POINT frames[kFactor * 2 + 1];
\r
9241 if (!appData.animate) return;
\r
9242 if (doingSizing) return;
\r
9243 if (fromY < 0 || fromX < 0) return;
\r
9244 piece = board[fromY][fromX];
\r
9245 if (piece >= EmptySquare) return;
\r
9247 ScreenSquare(fromX, fromY, &start);
\r
9248 ScreenSquare(toX, toY, &finish);
\r
9250 /* All pieces except knights move in straight line */
\r
9251 if (piece != WhiteKnight && piece != BlackKnight) {
\r
9252 mid.x = start.x + (finish.x - start.x) / 2;
\r
9253 mid.y = start.y + (finish.y - start.y) / 2;
\r
9255 /* Knight: make diagonal movement then straight */
\r
9256 if (abs(toY - fromY) < abs(toX - fromX)) {
\r
9257 mid.x = start.x + (finish.x - start.x) / 2;
\r
9261 mid.y = start.y + (finish.y - start.y) / 2;
\r
9265 /* Don't use as many frames for very short moves */
\r
9266 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
\r
9267 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
\r
9269 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
\r
9271 animInfo.from.x = fromX;
\r
9272 animInfo.from.y = fromY;
\r
9273 animInfo.to.x = toX;
\r
9274 animInfo.to.y = toY;
\r
9275 animInfo.lastpos = start;
\r
9276 animInfo.piece = piece;
\r
9277 for (n = 0; n < nFrames; n++) {
\r
9278 animInfo.pos = frames[n];
\r
9279 DrawPosition(FALSE, NULL);
\r
9280 animInfo.lastpos = animInfo.pos;
\r
9281 Sleep(appData.animSpeed);
\r
9283 animInfo.pos = finish;
\r
9284 DrawPosition(FALSE, NULL);
\r
9285 animInfo.piece = EmptySquare;
\r
9286 if(gameInfo.variant == VariantAtomic &&
\r
9287 (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )
\r
9288 AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);
\r
9291 /* Convert board position to corner of screen rect and color */
\r
9294 ScreenSquare(column, row, pt)
\r
9295 int column; int row; POINT * pt;
\r
9298 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
\r
9299 pt->y = lineGap + row * (squareSize + lineGap);
\r
9301 pt->x = lineGap + column * (squareSize + lineGap);
\r
9302 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
\r
9306 /* Generate a series of frame coords from start->mid->finish.
\r
9307 The movement rate doubles until the half way point is
\r
9308 reached, then halves back down to the final destination,
\r
9309 which gives a nice slow in/out effect. The algorithmn
\r
9310 may seem to generate too many intermediates for short
\r
9311 moves, but remember that the purpose is to attract the
\r
9312 viewers attention to the piece about to be moved and
\r
9313 then to where it ends up. Too few frames would be less
\r
9317 Tween(start, mid, finish, factor, frames, nFrames)
\r
9318 POINT * start; POINT * mid;
\r
9319 POINT * finish; int factor;
\r
9320 POINT frames[]; int * nFrames;
\r
9322 int n, fraction = 1, count = 0;
\r
9324 /* Slow in, stepping 1/16th, then 1/8th, ... */
\r
9325 for (n = 0; n < factor; n++)
\r
9327 for (n = 0; n < factor; n++) {
\r
9328 frames[count].x = start->x + (mid->x - start->x) / fraction;
\r
9329 frames[count].y = start->y + (mid->y - start->y) / fraction;
\r
9331 fraction = fraction / 2;
\r
9335 frames[count] = *mid;
\r
9338 /* Slow out, stepping 1/2, then 1/4, ... */
\r
9340 for (n = 0; n < factor; n++) {
\r
9341 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
\r
9342 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
\r
9344 fraction = fraction * 2;
\r
9350 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
\r
9352 MoveHistorySet( movelist, first, last, current, pvInfoList );
\r
9354 EvalGraphSet( first, last, current, pvInfoList );
\r