2 * WinBoard.c -- Windows NT front end to XBoard
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6 * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
8 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
9 * which was written and is copyrighted by Wayne Christopher.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard distributed
34 * by the Free Software Foundation:
35 * ------------------------------------------------------------------------
36 * This program is free software; you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation; either version 2 of the License, or
39 * (at your option) any later version.
41 * This program is distributed in the hope that it will be useful,
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 * GNU General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program; if not, write to the Free Software
48 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
49 * ------------------------------------------------------------------------
82 #include "wgamelist.h"
83 #include "wedittags.h"
97 POINT pos; /* window coordinates of current pos */
98 POINT lastpos; /* window coordinates of last pos - used for clipping */
99 POINT from; /* board coordinates of the piece's orig pos */
100 POINT to; /* board coordinates of the piece's new pos */
103 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
106 POINT start; /* window coordinates of start pos */
107 POINT pos; /* window coordinates of current pos */
108 POINT lastpos; /* window coordinates of last pos - used for clipping */
109 POINT from; /* board coordinates of the piece's orig pos */
112 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
115 POINT sq[2]; /* board coordinates of from, to squares */
118 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
119 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
121 /* Window class names */
122 char szAppName[] = "WinBoard";
123 char szConsoleName[] = "WBConsole";
126 char szTitle[] = "WinBoard";
127 char szConsoleTitle[] = "ICS Interaction";
130 char *settingsFileName;
131 BOOLEAN saveSettingsOnExit;
132 char installDir[MSG_SIZ];
135 BOOLEAN chessProgram;
136 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;
137 static int squareSize, lineGap;
138 static int winWidth, winHeight;
139 static RECT messageRect, whiteRect, blackRect;
140 static char messageText[MESSAGE_TEXT_MAX];
141 static int clockTimerEvent = 0;
142 static int loadGameTimerEvent = 0;
143 static DelayedEventCallback delayedTimerCallback;
144 static int delayedTimerEvent = 0;
145 static int buttonCount = 2;
146 char *icsTextMenuString;
148 char *firstChessProgramNames;
149 char *secondChessProgramNames;
151 static int analysisTimerEvent = 0;
153 #define ARG_MAX 20000
155 #define PALETTESIZE 256
158 extern void GuiCommand P((int command, int param));
160 HINSTANCE hInst; /* current instance */
161 HWND hwndMain = NULL; /* root window*/
162 HWND hwndConsole = NULL;
164 BOOLEAN alwaysOnTop = FALSE;
166 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
167 blackPieceColor, highlightSquareColor, premoveHighlightColor;
169 ColorClass currentColorClass;
171 HWND hCommPort = NULL; /* currently open comm port */
172 static HWND hwndPause; /* pause button */
173 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];
174 static HBRUSH lightSquareBrush, darkSquareBrush,
175 whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;
176 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];
177 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];
178 static HPEN gridPen = NULL;
179 static HPEN highlightPen = NULL;
180 static HPEN premovePen = NULL;
181 static NPLOGPALETTE pLogPal;
182 static BOOL paletteChanged = FALSE;
183 static HICON iconWhite, iconBlack, iconCurrent;
184 static int doingSizing = FALSE;
185 static int lastSizing = 0;
187 #if __GNUC__ && !defined(_winmajor)
188 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
190 #define oldDialog (_winmajor < 4)
193 char *defaultTextAttribs[] =
195 COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,
196 COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,
206 int cliWidth, cliHeight;
209 SizeInfo sizeInfo[] =
211 { "tiny", 21, 0, 1, 1, 0, 0 },
212 { "teeny", 25, 1, 1, 1, 0, 0 },
213 { "dinky", 29, 1, 1, 1, 0, 0 },
214 { "petite", 33, 1, 1, 1, 0, 0 },
215 { "slim", 37, 2, 1, 0, 0, 0 },
216 { "small", 40, 2, 1, 0, 0, 0 },
217 { "mediocre", 45, 2, 1, 0, 0, 0 },
218 { "middling", 49, 2, 0, 0, 0, 0 },
219 { "average", 54, 2, 0, 0, 0, 0 },
220 { "moderate", 58, 3, 0, 0, 0, 0 },
221 { "medium", 64, 3, 0, 0, 0, 0 },
222 { "bulky", 72, 3, 0, 0, 0, 0 },
223 { "large", 80, 3, 0, 0, 0, 0 },
224 { "big", 87, 3, 0, 0, 0, 0 },
225 { "huge", 95, 3, 0, 0, 0, 0 },
226 { "giant", 108, 3, 0, 0, 0, 0 },
227 { "colossal", 116, 4, 0, 0, 0, 0 },
228 { "titanic", 129, 4, 0, 0, 0, 0 },
229 { NULL, 0, 0, 0, 0, 0, 0 }
232 #define MF(x) {x, {0, }, {0, }, 0}
233 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
235 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY),
236 MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),
237 MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },
238 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY),
239 MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),
240 MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },
241 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),
242 MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),
243 MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },
244 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),
245 MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),
246 MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },
247 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),
248 MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),
249 MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },
250 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),
251 MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),
252 MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },
253 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),
254 MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),
255 MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },
256 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),
257 MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),
258 MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },
259 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),
260 MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),
261 MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },
262 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),
263 MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),
264 MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },
265 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),
266 MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),
267 MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },
268 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),
269 MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),
270 MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },
271 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),
272 MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),
273 MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },
274 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),
275 MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),
276 MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },
277 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),
278 MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),
279 MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },
280 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),
281 MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),
282 MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },
283 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),
284 MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),
285 MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },
286 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),
287 MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),
288 MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },
291 MyFont *font[NUM_SIZES][NUM_FONTS];
300 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
303 MyButtonDesc buttonDesc[N_BUTTONS] =
305 {"<<", IDM_ToStart, NULL, NULL},
306 {"<", IDM_Backward, NULL, NULL},
307 {"P", IDM_Pause, NULL, NULL},
308 {">", IDM_Forward, NULL, NULL},
309 {">>", IDM_ToEnd, NULL, NULL},
312 int tinyLayout = 0, smallLayout = 0;
313 #define MENU_BAR_ITEMS 6
314 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
315 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
316 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
320 MySound sounds[(int)NSoundClasses];
321 MyTextAttribs textAttribs[(int)NColorClasses];
323 MyColorizeAttribs colorizeAttribs[] = {
324 { (COLORREF)0, 0, "Shout Text" },
325 { (COLORREF)0, 0, "SShout/CShout" },
326 { (COLORREF)0, 0, "Channel 1 Text" },
327 { (COLORREF)0, 0, "Channel Text" },
328 { (COLORREF)0, 0, "Kibitz Text" },
329 { (COLORREF)0, 0, "Tell Text" },
330 { (COLORREF)0, 0, "Challenge Text" },
331 { (COLORREF)0, 0, "Request Text" },
332 { (COLORREF)0, 0, "Seek Text" },
333 { (COLORREF)0, 0, "Normal Text" },
334 { (COLORREF)0, 0, "None" }
339 static char *commentTitle;
340 static char *commentText;
341 static int commentIndex;
342 static Boolean editComment = FALSE;
343 HWND commentDialog = NULL;
344 BOOLEAN commentDialogUp = FALSE;
345 static int commentX, commentY, commentH, commentW;
347 HWND analysisDialog = NULL;
348 BOOLEAN analysisDialogUp = FALSE;
349 static int analysisX, analysisY, analysisH, analysisW;
351 char errorMessage[2*MSG_SIZ];
352 HWND errorDialog = NULL;
353 BOOLEAN moveErrorMessageUp = FALSE;
354 BOOLEAN consoleEcho = TRUE;
355 CHARFORMAT consoleCF;
356 COLORREF consoleBackgroundColor;
358 char *programVersion;
375 SOCKET sock2; /* stderr socket for OpenRcmd */
378 #define INPUT_SOURCE_BUF_SIZE 4096
380 typedef struct _InputSource {
387 char buf[INPUT_SOURCE_BUF_SIZE];
392 struct _InputSource *second; /* for stderr thread on CPRcmd */
396 InputSource *consoleInputSource;
401 VOID ConsoleOutput(char* data, int length, int forceVisible);
402 VOID ConsoleCreate();
404 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
406 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
407 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
408 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
409 VOID ParseCommSettings(char *arg, DCB *dcb);
412 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
413 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
414 void ParseIcsTextMenu(char *icsTextMenuString);
415 VOID PopUpMoveDialog(char firstchar);
416 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
419 * Setting "frozen" should disable all user input other than deleting
420 * the window. We do this while engines are initializing themselves.
422 static int frozen = 0;
423 static int oldMenuItemState[MENU_BAR_ITEMS];
431 hmenu = GetMenu(hwndMain);
432 for (i=0; i<MENU_BAR_ITEMS; i++) {
433 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
435 DrawMenuBar(hwndMain);
438 /* Undo a FreezeUI */
446 hmenu = GetMenu(hwndMain);
447 for (i=0; i<MENU_BAR_ITEMS; i++) {
448 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
450 DrawMenuBar(hwndMain);
453 /*---------------------------------------------------------------------------*\
457 \*---------------------------------------------------------------------------*/
460 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
461 LPSTR lpCmdLine, int nCmdShow)
464 HANDLE hAccelMain, hAccelNoAlt;
468 LoadLibrary("RICHED32.DLL");
469 consoleCF.cbSize = sizeof(CHARFORMAT);
470 if (!InitApplication(hInstance)) {
473 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
477 hAccelMain = LoadAccelerators (hInstance, szAppName);
478 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
480 /* Acquire and dispatch messages until a WM_QUIT message is received. */
482 while (GetMessage(&msg, /* message structure */
483 NULL, /* handle of window receiving the message */
484 0, /* lowest message to examine */
485 0)) /* highest message to examine */
487 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
488 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
489 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
490 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
491 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&
492 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
493 TranslateMessage(&msg); /* Translates virtual key codes */
494 DispatchMessage(&msg); /* Dispatches message to window */
499 return (msg.wParam); /* Returns the value from PostQuitMessage */
502 /*---------------------------------------------------------------------------*\
504 * Initialization functions
506 \*---------------------------------------------------------------------------*/
509 InitApplication(HINSTANCE hInstance)
513 /* Fill in window class structure with parameters that describe the */
516 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
517 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
518 wc.cbClsExtra = 0; /* No per-class extra data. */
519 wc.cbWndExtra = 0; /* No per-window extra data. */
520 wc.hInstance = hInstance; /* Owner of this class */
521 wc.hIcon = LoadIcon(hInstance, "icon_white");
522 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
523 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
524 wc.lpszMenuName = szAppName; /* Menu name from .RC */
525 wc.lpszClassName = szAppName; /* Name to register as */
527 /* Register the window class and return success/failure code. */
528 if (!RegisterClass(&wc)) return FALSE;
530 wc.style = CS_HREDRAW | CS_VREDRAW;
531 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
533 wc.cbWndExtra = DLGWINDOWEXTRA;
534 wc.hInstance = hInstance;
535 wc.hIcon = LoadIcon(hInstance, "icon_white");
536 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
537 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
538 wc.lpszMenuName = NULL;
539 wc.lpszClassName = szConsoleName;
541 if (!RegisterClass(&wc)) return FALSE;
545 /* Set by InitInstance, used by EnsureOnScreen */
546 int screenHeight, screenWidth;
549 EnsureOnScreen(int *x, int *y)
551 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
552 if (*x > screenWidth - 32) *x = 0;
553 if (*y > screenHeight - 32) *y = 0;
557 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
559 HWND hwnd; /* Main window handle. */
564 hInst = hInstance; /* Store instance handle in our global variable */
566 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
567 *filepart = NULLCHAR;
569 GetCurrentDirectory(MSG_SIZ, installDir);
571 InitAppData(lpCmdLine); /* Get run-time parameters */
572 if (appData.debugMode) {
573 debugFP = fopen("winboard.debug", "w");
574 setbuf(debugFP, NULL);
579 /* Create a main window for this application instance. */
580 hwnd = CreateWindow(szAppName, szTitle,
581 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
582 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
583 NULL, NULL, hInstance, NULL);
586 /* If window could not be created, return "failure" */
591 iconWhite = LoadIcon(hInstance, "icon_white");
592 iconBlack = LoadIcon(hInstance, "icon_black");
593 iconCurrent = iconWhite;
595 screenHeight = GetSystemMetrics(SM_CYSCREEN);
596 screenWidth = GetSystemMetrics(SM_CXSCREEN);
597 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
598 /* Compute window size for each board size, and use the largest
599 size that fits on this screen as the default. */
600 InitDrawingSizes((BoardSize)ibs, 0);
601 if (boardSize == (BoardSize)-1 &&
602 winHeight <= screenHeight && winWidth <= screenWidth) {
603 boardSize = (BoardSize)ibs;
606 InitDrawingSizes(boardSize, 0);
608 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
612 /* Make the window visible; update its client area; and return "success" */
613 EnsureOnScreen(&boardX, &boardY);
614 wp.length = sizeof(WINDOWPLACEMENT);
616 wp.showCmd = nCmdShow;
617 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
618 wp.rcNormalPosition.left = boardX;
619 wp.rcNormalPosition.right = boardX + winWidth;
620 wp.rcNormalPosition.top = boardY;
621 wp.rcNormalPosition.bottom = boardY + winHeight;
622 SetWindowPlacement(hwndMain, &wp);
624 SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
625 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
628 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
629 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
631 ShowWindow(hwndConsole, nCmdShow);
641 ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone,
642 ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,
651 String *pString; // ArgString
653 float *pFloat; // ArgFloat
654 Boolean *pBoolean; // ArgBoolean
655 COLORREF *pColor; // ArgColor
656 ColorClass cc; // ArgAttribs
657 String *pFilename; // ArgFilename
658 BoardSize *pBoardSize; // ArgBoardSize
659 int whichFont; // ArgFont
660 DCB *pDCB; // ArgCommSettings
661 String *pFilename; // ArgSettingsFilename
669 ArgDescriptor argDescriptors[] = {
670 /* positional arguments */
671 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
672 { "", ArgNone, NULL },
673 /* keyword arguments */
674 { "EngineRoom", ArgBoolean, (LPVOID) &appData.AnalysisWindow, TRUE },
675 { "eRoom", ArgTrue, (LPVOID) &appData.AnalysisWindow, FALSE },
676 { "xeRoom", ArgFalse, (LPVOID) &appData.AnalysisWindow, FALSE },
677 { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },
678 { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },
679 { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },
680 { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },
681 { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },
682 { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },
683 { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },
684 { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },
685 { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },
686 { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },
687 { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },
688 { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },
689 { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },
690 { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },
691 { "initString", ArgString, (LPVOID) &appData.initString, FALSE },
692 { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },
693 { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },
694 { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,
696 { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,
698 { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,
700 { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },
701 { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,
703 { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },
704 { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },
705 { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },
706 { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
707 { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
708 { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },
709 { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },
710 { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
711 { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
712 { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },
713 { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },
714 { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },
715 { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },
716 { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
717 { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
718 { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
719 { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
720 /*!!bitmapDirectory?*/
721 { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
722 { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
723 { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
724 { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
725 { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },
726 { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },
727 { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },
728 { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },
729 { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },
730 { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },
731 { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },
732 { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },
733 { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
734 { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
735 { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },
736 { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },
737 { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },
738 { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },
739 { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
740 { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
741 { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
742 { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
743 { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
744 { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
745 { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },
746 { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },
747 { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
748 { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
749 { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },
750 { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },
751 { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },
752 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
753 { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
754 { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
755 { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
756 { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },
757 { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },
758 { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },
759 { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },
760 { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
761 { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
762 { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
763 { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
764 { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
765 { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
766 { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
767 { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
768 { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },
769 { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },
770 { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
771 { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
772 { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },
773 { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },
774 { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },
775 { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },
776 { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
777 { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
778 { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },
779 { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },
780 { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
781 { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
782 { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },
783 { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },
784 { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
785 { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
786 { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },
787 { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },
788 { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
789 { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
790 { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },
791 { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },
792 { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
793 { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
794 { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },
795 { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },
796 { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
797 { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
798 { "icc", ArgTrue, (LPVOID) &appData.ICC_feature, FALSE },
799 { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },
800 { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },
801 { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
802 { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
803 { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },
804 { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },
805 { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
806 { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
807 { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },
808 { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },
809 { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
810 { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
811 { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },
812 { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },
813 { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
814 { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
815 { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors,
816 FALSE }, /* only so that old WinBoard.ini files from betas can be read */
817 { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },
818 { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },
819 { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },
820 { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },
821 { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },
822 { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },
823 { "boardSize", ArgBoardSize, (LPVOID) &boardSize,
824 TRUE }, /* must come after all fonts */
825 { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },
826 { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,
827 FALSE }, /* historical; kept only so old winboard.ini files will parse */
828 { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },
829 { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },
830 { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
831 { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
832 { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },
833 { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },
834 { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
835 { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
836 { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },
837 { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },
838 { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
839 { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
840 { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },
841 { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },
842 { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
843 { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
844 { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },
845 { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },
846 { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
847 { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
848 { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },
849 { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },
850 { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
851 { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
852 { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },
853 { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },
854 { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
855 { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
857 { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
858 { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
860 { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },
861 { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
862 { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
863 { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
864 { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },
865 { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },
866 { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
867 { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
868 { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },
869 { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },
870 { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
871 { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
872 { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },
873 { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },
874 { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
875 { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
876 { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },
877 { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },
878 { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
879 { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
880 { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },
881 { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },
882 { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },
883 { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },
884 { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },
885 { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },
886 { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
887 { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
888 { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },
889 { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },
890 { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },
891 { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
892 { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
893 { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },
894 { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},
895 { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},
896 { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
897 { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
898 { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},
899 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
900 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
901 { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },
902 { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
903 { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
904 { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },
905 { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },
906 { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },
907 { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },
908 { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },
909 { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },
910 { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },
911 { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
912 { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
913 { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },
914 { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },
915 { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
916 { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
917 { "highlightLastMove", ArgBoolean,
918 (LPVOID) &appData.highlightLastMove, TRUE },
919 { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },
920 { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
921 { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
922 { "highlightDragging", ArgBoolean,
923 (LPVOID) &appData.highlightDragging, TRUE },
924 { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },
925 { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
926 { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
927 { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },
928 { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },
929 { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
930 { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
931 { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },
932 { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },
933 { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },
934 { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },
935 { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },
936 { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },
937 { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },
938 { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },
939 { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },
940 { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },
941 { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },
942 { "soundShout", ArgFilename,
943 (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },
944 { "soundSShout", ArgFilename,
945 (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },
946 { "soundChannel1", ArgFilename,
947 (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },
948 { "soundChannel", ArgFilename,
949 (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },
950 { "soundKibitz", ArgFilename,
951 (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },
952 { "soundTell", ArgFilename,
953 (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },
954 { "soundChallenge", ArgFilename,
955 (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },
956 { "soundRequest", ArgFilename,
957 (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },
958 { "soundSeek", ArgFilename,
959 (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },
960 { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },
961 { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },
962 { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },
963 { "soundIcsLoss", ArgFilename,
964 (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },
965 { "soundIcsDraw", ArgFilename,
966 (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },
967 { "soundIcsUnfinished", ArgFilename,
968 (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},
969 { "soundIcsAlarm", ArgFilename,
970 (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },
971 { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },
972 { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },
973 { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
974 { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
975 { "reuseChessPrograms", ArgBoolean,
976 (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */
977 { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },
978 { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },
979 { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
980 { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
981 { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },
982 { "x", ArgInt, (LPVOID) &boardX, TRUE },
983 { "y", ArgInt, (LPVOID) &boardY, TRUE },
984 { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },
985 { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },
986 { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },
987 { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },
988 { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },
989 { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },
990 { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },
991 { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },
992 { "commentX", ArgInt, (LPVOID) &commentX, TRUE },
993 { "commentY", ArgInt, (LPVOID) &commentY, TRUE },
994 { "commentW", ArgInt, (LPVOID) &commentW, TRUE },
995 { "commentH", ArgInt, (LPVOID) &commentH, TRUE },
996 { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },
997 { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },
998 { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },
999 { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },
1000 { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },
1001 { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },
1002 { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },
1003 { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },
1004 { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
1005 { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
1006 { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },
1007 { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },
1008 { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },
1009 { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },
1010 { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },
1011 { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },
1012 { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },
1013 { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,
1015 { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,
1017 { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1018 { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1019 { "variant", ArgString, (LPVOID) &appData.variant, FALSE },
1020 { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,
1022 { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,
1024 { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },
1025 { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },
1026 { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1027 { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1029 { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },
1030 { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },
1031 { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1032 { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1033 { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },
1034 { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },
1035 { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1036 { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1037 { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },
1038 { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },
1039 { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },
1040 { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },
1041 { "zippyPassword3", ArgString, (LPVOID) &appData.zippyPassword3, FALSE },
1042 { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,
1044 { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },
1045 { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },
1046 { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },
1047 { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1048 { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1049 { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },
1050 { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,
1052 { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1053 { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1054 { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1055 { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },
1056 { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },
1057 { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },
1058 { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },
1059 { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1060 { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1061 { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },
1062 { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },
1063 { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1064 { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1065 { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },
1066 { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },
1067 { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },
1068 /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */
1069 { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },
1071 { NULL, ArgNone, NULL, FALSE }
1075 /* Kludge for indirection files on command line */
1076 char* lastIndirectionFilename;
1077 ArgDescriptor argDescriptorIndirection =
1078 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };
1082 ExitArgError(char *msg, char *badArg)
1086 sprintf(buf, "%s %s", msg, badArg);
1087 DisplayFatalError(buf, 0, 2);
1091 /* Command line font name parser. NULL name means do nothing.
1092 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
1093 For backward compatibility, syntax without the colon is also
1094 accepted, but font names with digits in them won't work in that case.
1097 ParseFontName(char *name, MyFontParams *mfp)
1100 if (name == NULL) return;
1104 if (q - p >= sizeof(mfp->faceName))
1105 ExitArgError("Font name too long:", name);
1106 memcpy(mfp->faceName, p, q - p);
1107 mfp->faceName[q - p] = NULLCHAR;
1111 while (*p && !isdigit(*p)) {
1113 if (q - mfp->faceName >= sizeof(mfp->faceName))
1114 ExitArgError("Font name too long:", name);
1116 while (q > mfp->faceName && q[-1] == ' ') q--;
1119 if (!*p) ExitArgError("Font point size missing:", name);
1120 mfp->pointSize = (float) atof(p);
1121 mfp->bold = (strchr(p, 'b') != NULL);
1122 mfp->italic = (strchr(p, 'i') != NULL);
1123 mfp->underline = (strchr(p, 'u') != NULL);
1124 mfp->strikeout = (strchr(p, 's') != NULL);
1127 /* Color name parser.
1128 X version accepts X color names, but this one
1129 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
1131 ParseColorName(char *name)
1133 int red, green, blue, count;
1136 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
1138 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
1139 &red, &green, &blue);
1142 sprintf(buf, "Can't parse color name %s", name);
1143 DisplayError(buf, 0);
1144 return RGB(0, 0, 0);
1146 return PALETTERGB(red, green, blue);
1150 void ParseAttribs(COLORREF *color, int *effects, char* argValue)
1156 if (*e == 'b') eff |= CFE_BOLD;
1157 else if (*e == 'i') eff |= CFE_ITALIC;
1158 else if (*e == 'u') eff |= CFE_UNDERLINE;
1159 else if (*e == 's') eff |= CFE_STRIKEOUT;
1160 else if (*e == '#' || isdigit(*e)) break;
1164 *color = ParseColorName(e);
1169 ParseBoardSize(char *name)
1171 BoardSize bs = SizeTiny;
1172 while (sizeInfo[bs].name != NULL) {
1173 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;
1176 ExitArgError("Unrecognized board size value", name);
1177 return bs; /* not reached */
1182 StringGet(void *getClosure)
1184 char **p = (char **) getClosure;
1189 FileGet(void *getClosure)
1192 FILE* f = (FILE*) getClosure;
1201 /* Parse settings file named "name". If file found, return the
1202 full name in fullname and return TRUE; else return FALSE */
1204 ParseSettingsFile(char *name, char fullname[MSG_SIZ])
1209 if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {
1210 f = fopen(fullname, "r");
1212 ParseArgs(FileGet, f);
1221 ParseArgs(GetFunc get, void *cl)
1223 char argName[ARG_MAX];
1224 char argValue[ARG_MAX];
1234 while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);
1235 if (ch == NULLCHAR) break;
1237 /* Comment to end of line */
1239 while (ch != '\n' && ch != NULLCHAR) ch = get(cl);
1241 } else if (ch == '/' || ch == '-') {
1244 while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&
1245 ch != '\n' && ch != '\t') {
1251 for (ad = argDescriptors; ad->argName != NULL; ad++)
1252 if (strcmp(ad->argName, argName + 1) == 0) break;
1254 if (ad->argName == NULL)
1255 ExitArgError("Unrecognized argument", argName);
1257 } else if (ch == '@') {
1258 /* Indirection file */
1259 ad = &argDescriptorIndirection;
1262 /* Positional argument */
1263 ad = &argDescriptors[posarg++];
1264 strcpy(argName, ad->argName);
1267 if (ad->argType == ArgTrue) {
1268 *(Boolean *) ad->argLoc = TRUE;
1271 if (ad->argType == ArgFalse) {
1272 *(Boolean *) ad->argLoc = FALSE;
1276 while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);
1277 if (ch == NULLCHAR || ch == '\n') {
1278 ExitArgError("No value provided for argument", argName);
1282 // Quoting with { }. No characters have to (or can) be escaped.
1283 // Thus the string cannot contain a '}' character.
1303 } else if (ch == '\'' || ch == '"') {
1304 // Quoting with ' ' or " ", with \ as escape character.
1305 // Inconvenient for long strings that may contain Windows filenames.
1331 if (ad->argType == ArgFilename
1332 || ad->argType == ArgSettingsFilename) {
1338 ExitArgError("Incomplete \\ escape in value for", argName);
1362 for (i = 0; i < 3; i++) {
1363 if (ch >= '0' && ch <= '7') {
1364 octval = octval*8 + (ch - '0');
1371 *q++ = (char) octval;
1382 while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {
1389 switch (ad->argType) {
1391 *(int *) ad->argLoc = atoi(argValue);
1395 *(float *) ad->argLoc = (float) atof(argValue);
1400 *(char **) ad->argLoc = strdup(argValue);
1403 case ArgSettingsFilename:
1405 char fullname[MSG_SIZ];
1406 if (ParseSettingsFile(argValue, fullname)) {
1407 if (ad->argLoc != NULL) {
1408 *(char **) ad->argLoc = strdup(fullname);
1411 if (ad->argLoc != NULL) {
1413 ExitArgError("Failed to open indirection file", argValue);
1420 switch (argValue[0]) {
1423 *(Boolean *) ad->argLoc = TRUE;
1427 *(Boolean *) ad->argLoc = FALSE;
1430 ExitArgError("Unrecognized boolean argument value", argValue);
1436 *(COLORREF *)ad->argLoc = ParseColorName(argValue);
1440 ColorClass cc = (ColorClass)ad->argLoc;
1441 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);
1446 *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);
1450 ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);
1453 case ArgCommSettings:
1454 ParseCommSettings(argValue, &dcb);
1458 ExitArgError("Unrecognized argument", argValue);
1465 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
1467 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
1468 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
1471 lf->lfEscapement = 0;
1472 lf->lfOrientation = 0;
1473 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
1474 lf->lfItalic = mfp->italic;
1475 lf->lfUnderline = mfp->underline;
1476 lf->lfStrikeOut = mfp->strikeout;
1477 lf->lfCharSet = DEFAULT_CHARSET;
1478 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
1479 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
1480 lf->lfQuality = DEFAULT_QUALITY;
1481 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
1482 strcpy(lf->lfFaceName, mfp->faceName);
1486 CreateFontInMF(MyFont *mf)
1488 LFfromMFP(&mf->lf, &mf->mfp);
1489 if (mf->hf) DeleteObject(mf->hf);
1490 mf->hf = CreateFontIndirect(&mf->lf);
1494 SetDefaultTextAttribs()
1497 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1498 ParseAttribs(&textAttribs[cc].color,
1499 &textAttribs[cc].effects,
1500 defaultTextAttribs[cc]);
1509 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1510 textAttribs[cc].sound.name = strdup("");
1511 textAttribs[cc].sound.data = NULL;
1513 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1514 sounds[sc].name = strdup("");
1515 sounds[sc].data = NULL;
1517 sounds[(int)SoundBell].name = strdup(SOUND_BELL);
1525 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1526 MyLoadSound(&textAttribs[cc].sound);
1528 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1529 MyLoadSound(&sounds[sc]);
1534 InitAppData(LPSTR lpCmdLine)
1537 char buf[ARG_MAX], currDir[MSG_SIZ];
1540 programName = szAppName;
1542 /* Initialize to defaults */
1543 lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);
1544 darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);
1545 whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);
1546 blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);
1547 highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);
1548 premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);
1549 consoleBackgroundColor = ParseColorName(COLOR_BKGD);
1550 SetDefaultTextAttribs();
1552 appData.movesPerSession = MOVES_PER_SESSION;
1553 appData.initString = INIT_STRING;
1554 appData.secondInitString = INIT_STRING;
1555 appData.firstComputerString = COMPUTER_STRING;
1556 appData.secondComputerString = COMPUTER_STRING;
1557 appData.firstChessProgram = FIRST_CHESS_PROGRAM;
1558 appData.secondChessProgram = SECOND_CHESS_PROGRAM;
1559 appData.firstPlaysBlack = FALSE;
1560 appData.noChessProgram = FALSE;
1561 chessProgram = FALSE;
1562 appData.firstHost = FIRST_HOST;
1563 appData.secondHost = SECOND_HOST;
1564 appData.firstDirectory = FIRST_DIRECTORY;
1565 appData.secondDirectory = SECOND_DIRECTORY;
1566 appData.bitmapDirectory = "";
1567 appData.remoteShell = REMOTE_SHELL;
1568 appData.remoteUser = "";
1569 appData.timeDelay = TIME_DELAY;
1570 appData.timeControl = TIME_CONTROL;
1571 appData.timeIncrement = TIME_INCREMENT;
1572 appData.icsActive = FALSE;
1573 appData.icsHost = "";
1574 appData.icsPort = ICS_PORT;
1575 appData.icsCommPort = ICS_COMM_PORT;
1576 appData.icsLogon = ICS_LOGON;
1577 appData.icsHelper = "";
1578 appData.useTelnet = FALSE;
1579 appData.telnetProgram = TELNET_PROGRAM;
1580 appData.gateway = "";
1581 appData.loadGameFile = "";
1582 appData.loadGameIndex = 0;
1583 appData.saveGameFile = "";
1584 appData.autoSaveGames = FALSE;
1585 appData.loadPositionFile = "";
1586 appData.loadPositionIndex = 1;
1587 appData.savePositionFile = "";
1588 appData.matchMode = FALSE;
1589 appData.matchGames = 0;
1590 appData.monoMode = FALSE;
1591 appData.debugMode = FALSE;
1592 appData.clockMode = TRUE;
1593 boardSize = (BoardSize) -1; /* determine by screen size */
1594 appData.Iconic = FALSE; /*unused*/
1595 appData.searchTime = "";
1596 appData.searchDepth = 0;
1597 appData.showCoords = FALSE;
1598 appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/
1599 appData.autoCallFlag = FALSE;
1600 appData.flipView = FALSE;
1601 appData.autoFlipView = TRUE;
1602 appData.cmailGameName = "";
1603 appData.alwaysPromoteToQueen = FALSE;
1604 appData.oldSaveStyle = FALSE;
1605 appData.quietPlay = FALSE;
1606 appData.showThinking = FALSE;
1607 appData.ButtonSendOutPutToICS = TRUE;
1608 appData.SendOutPutToICS = 1; /* default whisper */
1609 appData.icsAnalyze = FALSE;
1610 appData.icsEngineKillPV = FALSE;
1611 appData.smartQueue = TRUE; /* smart queue with IcsAnalyzeOutPut */
1612 appData.icsEngineWhisper = FALSE;
1613 appData.icsEngineKibitz = FALSE;
1614 appData.icsEngineTell = FALSE;
1615 appData.icsAnalyzeWindow = FALSE;
1616 appData.icsEngineNone = TRUE; /* don't send */
1617 appData.icsAnalyzeOutPut = 4; /* don't send */
1618 appData.icsKillPVs = 9; /* a recommend value for most programs */
1619 appData.windowMove = TRUE; /* Drop display fail low/high moves at ICS */
1620 appData.icsSmartQueue = 0; /* 0 = standard 1 = blitz */
1621 appData.icsShowBook = FALSE; /* show engine book */
1622 appData.userVersion = FALSE; /* programmer version false */
1623 appData.icsWBprotoAgr = FALSE;
1624 appData.icsWBprotoNorm = TRUE;
1625 appData.icsSmartQueueStd = TRUE;
1626 appData.icsSmartQueueBlitz = FALSE;
1627 appData.engineStatLine = TRUE; /* default send "." with engine room */
1628 appData.engineTourneyMode = FALSE; /* default no tourney mode */
1629 appData.zippyDraw = TRUE;
1630 appData.ponderNextMove = TRUE;
1631 appData.periodicUpdates = TRUE;
1632 appData.popupExitMessage = TRUE;
1633 appData.popupMoveErrors = FALSE;
1634 appData.autoObserve = FALSE;
1635 appData.autoComment = FALSE;
1636 appData.animate = TRUE;
1637 appData.animSpeed = 10;
1638 appData.animateDragging = TRUE;
1639 appData.highlightLastMove = TRUE;
1640 appData.getMoveList = TRUE;
1641 appData.testLegality = TRUE;
1642 appData.premove = TRUE;
1643 appData.premoveWhite = FALSE;
1644 appData.premoveWhiteText = "";
1645 appData.premoveBlack = FALSE;
1646 appData.premoveBlackText = "";
1647 appData.icsAlarm = TRUE;
1648 appData.icsAlarmTime = 5000;
1649 appData.autoRaiseBoard = TRUE;
1650 appData.localLineEditing = TRUE;
1651 appData.colorize = TRUE;
1652 appData.reuseFirst = TRUE;
1653 appData.reuseSecond = TRUE;
1654 appData.blindfold = FALSE;
1655 dcb.DCBlength = sizeof(DCB);
1656 dcb.BaudRate = 9600;
1658 dcb.fParity = FALSE;
1659 dcb.fOutxCtsFlow = FALSE;
1660 dcb.fOutxDsrFlow = FALSE;
1661 dcb.fDtrControl = DTR_CONTROL_ENABLE;
1662 dcb.fDsrSensitivity = FALSE;
1663 dcb.fTXContinueOnXoff = TRUE;
1667 dcb.fRtsControl = RTS_CONTROL_ENABLE;
1668 dcb.fAbortOnError = FALSE;
1671 dcb.Parity = SPACEPARITY;
1672 dcb.StopBits = ONESTOPBIT;
1673 settingsFileName = SETTINGS_FILE;
1674 saveSettingsOnExit = TRUE;
1675 boardX = CW_USEDEFAULT;
1676 boardY = CW_USEDEFAULT;
1677 consoleX = CW_USEDEFAULT;
1678 consoleY = CW_USEDEFAULT;
1679 consoleW = CW_USEDEFAULT;
1680 consoleH = CW_USEDEFAULT;
1681 analysisX = CW_USEDEFAULT;
1682 analysisY = CW_USEDEFAULT;
1685 commentX = CW_USEDEFAULT;
1686 commentY = CW_USEDEFAULT;
1687 commentW = CW_USEDEFAULT;
1688 commentH = CW_USEDEFAULT;
1689 editTagsX = CW_USEDEFAULT;
1690 editTagsY = CW_USEDEFAULT;
1691 editTagsW = CW_USEDEFAULT;
1692 editTagsH = CW_USEDEFAULT;
1693 gameListX = CW_USEDEFAULT;
1694 gameListY = CW_USEDEFAULT;
1695 gameListW = CW_USEDEFAULT;
1696 gameListH = CW_USEDEFAULT;
1697 icsTextMenuString = ICS_TEXT_MENU_DEFAULT;
1698 icsNames = ICS_NAMES;
1699 firstChessProgramNames = FCP_NAMES;
1700 secondChessProgramNames = SCP_NAMES;
1701 appData.initialMode = "";
1702 appData.variant = "normal";
1703 appData.firstProtocolVersion = PROTOVER;
1704 appData.secondProtocolVersion = PROTOVER;
1705 appData.showButtonBar = TRUE;
1707 appData.zippyTalk = ZIPPY_TALK;
1708 appData.zippyPlay = ZIPPY_PLAY;
1709 appData.zippyLines = ZIPPY_LINES;
1710 appData.zippyPinhead = ZIPPY_PINHEAD;
1711 appData.zippyPassword = ZIPPY_PASSWORD;
1712 appData.zippyPassword2 = ZIPPY_PASSWORD2;
1713 appData.zippyPassword3 = ZIPPY_PASSWORD3;
1714 appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;
1715 appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;
1716 appData.zippyUseI = ZIPPY_USE_I;
1717 appData.zippyBughouse = ZIPPY_BUGHOUSE;
1718 appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;
1719 appData.zippyGameEnd = ZIPPY_GAME_END;
1720 appData.zippyGameStart = ZIPPY_GAME_START;
1721 appData.zippyAdjourn = ZIPPY_ADJOURN;
1722 appData.zippyAbort = ZIPPY_ABORT;
1723 appData.zippyVariants = ZIPPY_VARIANTS;
1724 appData.zippyMaxGames = ZIPPY_MAX_GAMES;
1725 appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;
1728 /* Point font array elements to structures and
1729 parse default font names */
1730 for (i=0; i<NUM_FONTS; i++) {
1731 for (j=0; j<NUM_SIZES; j++) {
1732 font[j][i] = &fontRec[j][i];
1733 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
1737 /* Parse default settings file if any */
1738 if (ParseSettingsFile(settingsFileName, buf)) {
1739 settingsFileName = strdup(buf);
1742 /* Parse command line */
1743 ParseArgs(StringGet, &lpCmdLine);
1745 /* Propagate options that affect others */
1746 if (appData.matchMode || appData.matchGames) chessProgram = TRUE;
1747 if (appData.icsActive || appData.noChessProgram) {
1748 chessProgram = FALSE; /* not local chess program mode */
1751 /* Open startup dialog if needed */
1752 if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||
1753 (appData.icsActive && *appData.icsHost == NULLCHAR) ||
1754 (chessProgram && (*appData.firstChessProgram == NULLCHAR ||
1755 *appData.secondChessProgram == NULLCHAR))) {
1758 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
1759 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
1760 FreeProcInstance(lpProc);
1763 /* Make sure save files land in the right (?) directory */
1764 if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {
1765 appData.saveGameFile = strdup(buf);
1767 if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {
1768 appData.savePositionFile = strdup(buf);
1771 /* Finish initialization for fonts and sounds */
1772 for (i=0; i<NUM_FONTS; i++) {
1773 for (j=0; j<NUM_SIZES; j++) {
1774 CreateFontInMF(font[j][i]);
1777 /* xboard, and older WinBoards, controlled the move sound with the
1778 appData.ringBellAfterMoves option. In the current WinBoard, we
1779 always turn the option on (so that the backend will call us),
1780 then let the user turn the sound off by setting it to silence if
1781 desired. To accommodate old winboard.ini files saved by old
1782 versions of WinBoard, we also turn off the sound if the option
1783 was initially set to false. */
1784 if (!appData.ringBellAfterMoves) {
1785 sounds[(int)SoundMove].name = strdup("");
1786 appData.ringBellAfterMoves = TRUE;
1788 GetCurrentDirectory(MSG_SIZ, currDir);
1789 SetCurrentDirectory(installDir);
1791 SetCurrentDirectory(currDir);
1793 p = icsTextMenuString;
1795 FILE* f = fopen(p + 1, "r");
1797 DisplayFatalError(p + 1, errno, 2);
1800 i = fread(buf, 1, sizeof(buf)-1, f);
1805 ParseIcsTextMenu(strdup(p));
1812 HMENU hmenu = GetMenu(hwndMain);
1814 (void) EnableMenuItem(hmenu, IDM_CommPort,
1815 MF_BYCOMMAND|((appData.icsActive &&
1816 *appData.icsCommPort != NULLCHAR) ?
1817 MF_ENABLED : MF_GRAYED));
1818 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
1819 MF_BYCOMMAND|(saveSettingsOnExit ?
1820 MF_CHECKED : MF_UNCHECKED));
1825 SaveSettings(char* name)
1832 if (!hwndMain) return;
1834 GetCurrentDirectory(MSG_SIZ, dir);
1835 SetCurrentDirectory(installDir);
1836 f = fopen(name, "w");
1837 SetCurrentDirectory(dir);
1839 DisplayError(name, errno);
1843 fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);
1845 fprintf(f, "; You can edit the values of options that are already set in this file,\n");
1846 fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");
1847 fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");
1850 wp.length = sizeof(WINDOWPLACEMENT);
1851 GetWindowPlacement(hwndMain, &wp);
1852 boardX = wp.rcNormalPosition.left;
1853 boardY = wp.rcNormalPosition.top;
1856 GetWindowPlacement(hwndConsole, &wp);
1857 consoleX = wp.rcNormalPosition.left;
1858 consoleY = wp.rcNormalPosition.top;
1859 consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1860 consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1863 if (analysisDialog) {
1864 GetWindowPlacement(analysisDialog, &wp);
1865 analysisX = wp.rcNormalPosition.left;
1866 analysisY = wp.rcNormalPosition.top;
1867 analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1868 analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1871 if (commentDialog) {
1872 GetWindowPlacement(commentDialog, &wp);
1873 commentX = wp.rcNormalPosition.left;
1874 commentY = wp.rcNormalPosition.top;
1875 commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1876 commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1879 if (editTagsDialog) {
1880 GetWindowPlacement(editTagsDialog, &wp);
1881 editTagsX = wp.rcNormalPosition.left;
1882 editTagsY = wp.rcNormalPosition.top;
1883 editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1884 editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1887 if (gameListDialog) {
1888 GetWindowPlacement(gameListDialog, &wp);
1889 gameListX = wp.rcNormalPosition.left;
1890 gameListY = wp.rcNormalPosition.top;
1891 gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1892 gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1895 for (ad = argDescriptors; ad->argName != NULL; ad++) {
1896 if (!ad->save) continue;
1897 switch (ad->argType) {
1900 char *p = *(char **)ad->argLoc;
1901 if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {
1902 /* Quote multiline values or \-containing values
1903 with { } if possible */
1904 fprintf(f, "/%s={%s}\n", ad->argName, p);
1906 /* Else quote with " " */
1907 fprintf(f, "/%s=\"", ad->argName);
1909 if (*p == '\n') fprintf(f, "\n");
1910 else if (*p == '\r') fprintf(f, "\\r");
1911 else if (*p == '\t') fprintf(f, "\\t");
1912 else if (*p == '\b') fprintf(f, "\\b");
1913 else if (*p == '\f') fprintf(f, "\\f");
1914 else if (*p < ' ') fprintf(f, "\\%03o", *p);
1915 else if (*p == '\"') fprintf(f, "\\\"");
1916 else if (*p == '\\') fprintf(f, "\\\\");
1925 fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);
1928 fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);
1931 fprintf(f, "/%s=%s\n", ad->argName,
1932 (*(Boolean *)ad->argLoc) ? "true" : "false");
1935 if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1938 if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1942 COLORREF color = *(COLORREF *)ad->argLoc;
1943 fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName,
1944 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1949 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
1950 fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,
1951 (ta->effects & CFE_BOLD) ? "b" : "",
1952 (ta->effects & CFE_ITALIC) ? "i" : "",
1953 (ta->effects & CFE_UNDERLINE) ? "u" : "",
1954 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
1955 (ta->effects) ? " " : "",
1956 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
1960 if (strchr(*(char **)ad->argLoc, '\"')) {
1961 fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);
1963 fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);
1967 fprintf(f, "/%s=%s\n", ad->argName,
1968 sizeInfo[*(BoardSize *)ad->argLoc].name);
1973 for (bs=0; bs<NUM_SIZES; bs++) {
1974 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
1975 fprintf(f, "/size=%s ", sizeInfo[bs].name);
1976 fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",
1977 ad->argName, mfp->faceName, mfp->pointSize,
1978 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
1979 mfp->bold ? "b" : "",
1980 mfp->italic ? "i" : "",
1981 mfp->underline ? "u" : "",
1982 mfp->strikeout ? "s" : "");
1986 case ArgCommSettings:
1987 PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);
1995 /*---------------------------------------------------------------------------*\
1997 * GDI board drawing routines
1999 \*---------------------------------------------------------------------------*/
2002 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
2006 sprintf(name, "%s%d%s", piece, squareSize, suffix);
2007 if (gameInfo.event &&
2008 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
2009 strcmp(name, "k80s") == 0) {
2010 strcpy(name, "tim");
2012 return LoadBitmap(hinst, name);
2016 /* Insert a color into the program's logical palette
2017 structure. This code assumes the given color is
2018 the result of the RGB or PALETTERGB macro, and it
2019 knows how those macros work (which is documented).
2022 InsertInPalette(COLORREF color)
2024 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
2026 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
2027 DisplayFatalError("Too many colors", 0, 1);
2028 pLogPal->palNumEntries--;
2032 pe->peFlags = (char) 0;
2033 pe->peRed = (char) (0xFF & color);
2034 pe->peGreen = (char) (0xFF & (color >> 8));
2035 pe->peBlue = (char) (0xFF & (color >> 16));
2043 if (pLogPal == NULL) {
2044 /* Allocate enough memory for a logical palette with
2045 * PALETTESIZE entries and set the size and version fields
2046 * of the logical palette structure.
2048 pLogPal = (NPLOGPALETTE)
2049 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
2050 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
2051 pLogPal->palVersion = 0x300;
2053 pLogPal->palNumEntries = 0;
2055 InsertInPalette(lightSquareColor);
2056 InsertInPalette(darkSquareColor);
2057 InsertInPalette(whitePieceColor);
2058 InsertInPalette(blackPieceColor);
2059 InsertInPalette(highlightSquareColor);
2060 InsertInPalette(premoveHighlightColor);
2062 /* create a logical color palette according the information
2063 * in the LOGPALETTE structure.
2065 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
2067 lightSquareBrush = CreateSolidBrush(lightSquareColor);
2068 darkSquareBrush = CreateSolidBrush(darkSquareColor);
2069 whitePieceBrush = CreateSolidBrush(whitePieceColor);
2070 blackPieceBrush = CreateSolidBrush(blackPieceColor);
2071 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
2076 BoardWidth(int boardSize)
2078 return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +
2079 BOARD_SIZE * sizeInfo[boardSize].squareSize;
2082 /* Respond to board resize by dragging edge */
2084 ResizeBoard(int newSizeX, int newSizeY, int flags)
2086 BoardSize newSize = NUM_SIZES - 1;
2087 static int recurse = 0;
2088 if (IsIconic(hwndMain)) return;
2089 if (recurse > 0) return;
2091 while (newSize > 0 &&
2092 (newSizeX < sizeInfo[newSize].cliWidth ||
2093 newSizeY < sizeInfo[newSize].cliHeight)) {
2096 boardSize = newSize;
2097 InitDrawingSizes(boardSize, flags);
2104 InitDrawingSizes(BoardSize boardSize, int flags)
2108 static int oldBoardSize = -1, oldTinyLayout = 0;
2110 SIZE clockSize, messageSize;
2114 HMENU hmenu = GetMenu(hwndMain);
2119 tinyLayout = sizeInfo[boardSize].tinyLayout;
2120 smallLayout = sizeInfo[boardSize].smallLayout;
2121 squareSize = sizeInfo[boardSize].squareSize;
2122 lineGap = sizeInfo[boardSize].lineGap;
2124 if (tinyLayout != oldTinyLayout) {
2125 long style = GetWindowLong(hwndMain, GWL_STYLE);
2127 style &= ~WS_SYSMENU;
2128 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
2129 "&Minimize\tCtrl+F4");
2131 style |= WS_SYSMENU;
2132 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
2134 SetWindowLong(hwndMain, GWL_STYLE, style);
2136 for (i=0; menuBarText[tinyLayout][i]; i++) {
2137 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
2138 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
2140 DrawMenuBar(hwndMain);
2143 boardWidth = BoardWidth(boardSize);
2145 /* Get text area sizes */
2146 hdc = GetDC(hwndMain);
2147 if (appData.clockMode) {
2148 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
2150 sprintf(buf, "White");
2152 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
2153 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
2154 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2155 str = "We only care about the height here";
2156 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
2157 SelectObject(hdc, oldFont);
2158 ReleaseDC(hwndMain, hdc);
2160 /* Compute where everything goes */
2161 whiteRect.left = OUTER_MARGIN;
2162 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
2163 whiteRect.top = OUTER_MARGIN;
2164 whiteRect.bottom = whiteRect.top + clockSize.cy;
2166 blackRect.left = whiteRect.right + INNER_MARGIN;
2167 blackRect.right = blackRect.left + boardWidth/2 - 1;
2168 blackRect.top = whiteRect.top;
2169 blackRect.bottom = whiteRect.bottom;
2171 messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;
2172 if (appData.showButtonBar) {
2173 messageRect.right = blackRect.right
2174 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
2176 messageRect.right = blackRect.right;
2178 messageRect.top = whiteRect.bottom + INNER_MARGIN;
2179 messageRect.bottom = messageRect.top + messageSize.cy;
2181 boardRect.left = whiteRect.left;
2182 boardRect.right = boardRect.left + boardWidth;
2183 boardRect.top = messageRect.bottom + INNER_MARGIN;
2184 boardRect.bottom = boardRect.top + boardWidth;
2186 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
2187 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
2188 winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
2189 winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
2190 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
2191 GetWindowRect(hwndMain, &wrect);
2192 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2193 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2194 /* compensate if menu bar wrapped */
2195 GetClientRect(hwndMain, &crect);
2196 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
2200 SetWindowPos(hwndMain, NULL,
2201 wrect.right - winWidth, wrect.bottom - winHeight,
2202 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2207 SetWindowPos(hwndMain, NULL,
2208 wrect.left, wrect.bottom - winHeight,
2209 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2212 case WMSZ_BOTTOMLEFT:
2214 SetWindowPos(hwndMain, NULL,
2215 wrect.right - winWidth, wrect.top,
2216 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2219 case WMSZ_BOTTOMRIGHT:
2223 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2224 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2229 for (i = 0; i < N_BUTTONS; i++) {
2230 if (buttonDesc[i].hwnd != NULL) {
2231 DestroyWindow(buttonDesc[i].hwnd);
2232 buttonDesc[i].hwnd = NULL;
2234 if (appData.showButtonBar) {
2235 buttonDesc[i].hwnd =
2236 CreateWindow("BUTTON", buttonDesc[i].label,
2237 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON ,
2238 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
2239 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
2240 (HMENU) buttonDesc[i].id,
2241 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
2243 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
2244 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
2245 MAKELPARAM(FALSE, 0));
2247 if (buttonDesc[i].id == IDM_Pause)
2248 hwndPause = buttonDesc[i].hwnd;
2249 buttonDesc[i].wndproc = (WNDPROC)
2250 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
2253 if (gridPen != NULL) DeleteObject(gridPen);
2254 if (highlightPen != NULL) DeleteObject(highlightPen);
2255 if (premovePen != NULL) DeleteObject(premovePen);
2257 logbrush.lbStyle = BS_SOLID;
2258 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
2260 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2261 lineGap, &logbrush, 0, NULL);
2262 logbrush.lbColor = highlightSquareColor;
2264 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2265 lineGap, &logbrush, 0, NULL);
2267 logbrush.lbColor = premoveHighlightColor;
2269 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2270 lineGap, &logbrush, 0, NULL);
2272 for (i = 0; i < BOARD_SIZE + 1; i++) {
2273 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
2274 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;
2275 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
2276 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
2277 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
2278 BOARD_SIZE * (squareSize + lineGap);
2279 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =
2280 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +
2281 lineGap / 2 + (i * (squareSize + lineGap));
2282 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =
2283 boardRect.top + BOARD_SIZE * (squareSize + lineGap);
2284 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
2288 if (boardSize == oldBoardSize) return;
2289 oldBoardSize = boardSize;
2290 oldTinyLayout = tinyLayout;
2292 /* Load piece bitmaps for this board size */
2293 for (i=0; i<=2; i++) {
2294 for (piece = WhitePawn;
2295 (int) piece <= (int) WhiteKing;
2296 piece = (ChessSquare) ((int) piece + 1)) {
2297 if (pieceBitmap[i][piece] != NULL)
2298 DeleteObject(pieceBitmap[i][piece]);
2302 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
2303 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
2304 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
2305 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
2306 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
2307 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
2308 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
2309 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
2310 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
2311 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
2312 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
2313 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
2314 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
2315 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
2316 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
2317 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
2318 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
2319 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
2324 PieceBitmap(ChessSquare p, int kind)
2326 if ((int) p >= (int) BlackPawn)
2327 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
2329 return pieceBitmap[kind][(int) p];
2332 /***************************************************************/
2334 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
2335 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
2337 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
2338 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
2342 SquareToPos(int row, int column, int * x, int * y)
2345 *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
2346 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
2348 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
2349 *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
2354 DrawCoordsOnDC(HDC hdc)
2356 static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};
2357 static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};
2358 char str[2] = { NULLCHAR, NULLCHAR };
2359 int oldMode, oldAlign, x, y, start, i;
2363 if (!appData.showCoords)
2366 start = flipView ? 0 : 8;
2368 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
2369 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
2370 oldAlign = GetTextAlign(hdc);
2371 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
2373 y = boardRect.top + lineGap;
2374 x = boardRect.left + lineGap;
2376 SetTextAlign(hdc, TA_LEFT|TA_TOP);
2377 for (i = 0; i < 8; i++) {
2378 str[0] = files[start + i];
2379 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
2380 y += squareSize + lineGap;
2383 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
2384 for (i = 0; i < 8; i++) {
2385 str[0] = ranks[start + i];
2386 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
2387 x += squareSize + lineGap;
2390 SelectObject(hdc, oldBrush);
2391 SetBkMode(hdc, oldMode);
2392 SetTextAlign(hdc, oldAlign);
2393 SelectObject(hdc, oldFont);
2397 DrawGridOnDC(HDC hdc)
2402 oldPen = SelectObject(hdc, gridPen);
2403 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);
2404 SelectObject(hdc, oldPen);
2408 #define HIGHLIGHT_PEN 0
2409 #define PREMOVE_PEN 1
2412 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
2416 if (lineGap == 0) return;
2418 x1 = boardRect.left +
2419 lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);
2420 y1 = boardRect.top +
2421 lineGap/2 + y * (squareSize + lineGap);
2423 x1 = boardRect.left +
2424 lineGap/2 + x * (squareSize + lineGap);
2425 y1 = boardRect.top +
2426 lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);
2428 hPen = pen ? premovePen : highlightPen;
2429 oldPen = SelectObject(hdc, on ? hPen : gridPen);
2430 MoveToEx(hdc, x1, y1, NULL);
2431 LineTo(hdc, x1 + squareSize + lineGap, y1);
2432 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
2433 LineTo(hdc, x1, y1 + squareSize + lineGap);
2434 LineTo(hdc, x1, y1);
2435 SelectObject(hdc, oldPen);
2439 DrawHighlightsOnDC(HDC hdc)
2442 for (i=0; i<2; i++) {
2443 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
2444 DrawHighlightOnDC(hdc, TRUE,
2445 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
2448 for (i=0; i<2; i++) {
2449 if (premoveHighlightInfo.sq[i].x >= 0 &&
2450 premoveHighlightInfo.sq[i].y >= 0) {
2451 DrawHighlightOnDC(hdc, TRUE,
2452 premoveHighlightInfo.sq[i].x,
2453 premoveHighlightInfo.sq[i].y,
2459 /* Note: sqcolor is used only in monoMode */
2460 /* Note that this code is largely duplicated in woptions.c,
2461 function DrawSampleSquare, so that needs to be updated too */
2463 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
2468 if (appData.blindfold) return;
2470 if (appData.monoMode) {
2471 SelectObject(tmphdc, PieceBitmap(piece,
2472 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
2473 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
2474 sqcolor ? SRCCOPY : NOTSRCCOPY);
2477 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
2478 oldBrush = SelectObject(hdc, whitePieceBrush);
2479 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2481 /* Use black piece color for outline of white pieces */
2482 /* Not sure this looks really good (though xboard does it).
2483 Maybe better to have another selectable color, default black */
2484 SelectObject(hdc, blackPieceBrush); /* could have own brush */
2485 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2486 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2488 /* Use black for outline of white pieces */
2489 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2490 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);
2494 /* Use white piece color for details of black pieces */
2495 /* Requires filled-in solid bitmaps (BLACK_PIECE class); the
2496 WHITE_PIECE ones aren't always the right shape. */
2497 /* Not sure this looks really good (though xboard does it).
2498 Maybe better to have another selectable color, default medium gray? */
2499 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));
2500 oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */
2501 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2502 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2503 SelectObject(hdc, blackPieceBrush);
2504 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2506 /* Use square color for details of black pieces */
2507 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2508 oldBrush = SelectObject(hdc, blackPieceBrush);
2509 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2512 SelectObject(hdc, oldBrush);
2513 SelectObject(tmphdc, oldBitmap);
2518 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
2520 int row, column, x, y, square_color, piece_color;
2524 for (row = 0; row < BOARD_SIZE; row++) {
2525 for (column = 0; column < BOARD_SIZE; column++) {
2527 SquareToPos(row, column, &x, &y);
2529 piece = board[row][column];
2531 square_color = ((column + row) % 2) == 1;
2532 piece_color = (int) piece < (int) BlackPawn;
2534 if (appData.monoMode) {
2535 if (piece == EmptySquare) {
2536 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
2537 square_color ? WHITENESS : BLACKNESS);
2539 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
2542 oldBrush = SelectObject(hdc, square_color ?
2543 lightSquareBrush : darkSquareBrush);
2544 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
2545 SelectObject(hdc, oldBrush);
2546 if (piece != EmptySquare)
2547 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
2553 #define MAX_CLIPS 200 /* more than enough */
2556 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
2558 static Board lastReq, lastDrawn;
2559 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
2560 static int lastDrawnFlipView = 0;
2561 static int lastReqValid = 0, lastDrawnValid = 0;
2562 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
2565 HBITMAP bufferBitmap;
2568 HRGN clips[MAX_CLIPS];
2569 ChessSquare dragged_piece = EmptySquare;
2571 /* I'm undecided on this - this function figures out whether a full
2572 * repaint is necessary on its own, so there's no real reason to have the
2573 * caller tell it that. I think this can safely be set to FALSE - but
2574 * if we trust the callers not to request full repaints unnessesarily, then
2575 * we could skip some clipping work. In other words, only request a full
2576 * redraw when the majority of pieces have changed positions (ie. flip,
2577 * gamestart and similar) --Hawk
2579 Boolean fullrepaint = repaint;
2581 if (board == NULL) {
2582 if (!lastReqValid) {
2587 CopyBoard(lastReq, board);
2595 if (IsIconic(hwndMain)) {
2600 hdc = GetDC(hwndMain);
2601 if (!appData.monoMode) {
2602 SelectPalette(hdc, hPal, FALSE);
2603 RealizePalette(hdc);
2611 fprintf(debugFP, "*******************************\n"
2613 "dragInfo.from (%d,%d)\n"
2614 "dragInfo.start (%d,%d)\n"
2615 "dragInfo.pos (%d,%d)\n"
2616 "dragInfo.lastpos (%d,%d)\n",
2617 repaint ? "TRUE" : "FALSE",
2618 dragInfo.from.x, dragInfo.from.y,
2619 dragInfo.start.x, dragInfo.start.y,
2620 dragInfo.pos.x, dragInfo.pos.y,
2621 dragInfo.lastpos.x, dragInfo.lastpos.y);
2622 fprintf(debugFP, "prev: ");
2623 for (row = 0; row < 8; row++) {
2624 for (column = 0; column < 8; column++) {
2625 fprintf(debugFP, "%d ", lastDrawn[row][column]);
2628 fprintf(debugFP, "\n");
2629 fprintf(debugFP, "board: ");
2630 for (row = 0; row < 8; row++) {
2631 for (column = 0; column < 8; column++) {
2632 fprintf(debugFP, "%d ", board[row][column]);
2635 fprintf(debugFP, "\n");
2639 /* Create some work-DCs */
2640 hdcmem = CreateCompatibleDC(hdc);
2641 tmphdc = CreateCompatibleDC(hdc);
2643 /* Figure out which squares need updating by comparing the
2644 * newest board with the last drawn board and checking if
2645 * flipping has changed.
2647 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
2648 for (row = 0; row < 8; row++) {
2649 for (column = 0; column < 8; column++) {
2650 if (lastDrawn[row][column] != board[row][column]) {
2651 SquareToPos(row, column, &x, &y);
2652 clips[num_clips++] =
2653 CreateRectRgn(x, y, x + squareSize, y + squareSize);
2657 for (i=0; i<2; i++) {
2658 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
2659 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
2660 if (lastDrawnHighlight.sq[i].x >= 0 &&
2661 lastDrawnHighlight.sq[i].y >= 0) {
2662 SquareToPos(lastDrawnHighlight.sq[i].y,
2663 lastDrawnHighlight.sq[i].x, &x, &y);
2664 clips[num_clips++] =
2665 CreateRectRgn(x - lineGap, y - lineGap,
2666 x + squareSize + lineGap, y + squareSize + lineGap);
2668 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
2669 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
2670 clips[num_clips++] =
2671 CreateRectRgn(x - lineGap, y - lineGap,
2672 x + squareSize + lineGap, y + squareSize + lineGap);
2676 for (i=0; i<2; i++) {
2677 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
2678 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
2679 if (lastDrawnPremove.sq[i].x >= 0 &&
2680 lastDrawnPremove.sq[i].y >= 0) {
2681 SquareToPos(lastDrawnPremove.sq[i].y,
2682 lastDrawnPremove.sq[i].x, &x, &y);
2683 clips[num_clips++] =
2684 CreateRectRgn(x - lineGap, y - lineGap,
2685 x + squareSize + lineGap, y + squareSize + lineGap);
2687 if (premoveHighlightInfo.sq[i].x >= 0 &&
2688 premoveHighlightInfo.sq[i].y >= 0) {
2689 SquareToPos(premoveHighlightInfo.sq[i].y,
2690 premoveHighlightInfo.sq[i].x, &x, &y);
2691 clips[num_clips++] =
2692 CreateRectRgn(x - lineGap, y - lineGap,
2693 x + squareSize + lineGap, y + squareSize + lineGap);
2701 /* Create a buffer bitmap - this is the actual bitmap
2702 * being written to. When all the work is done, we can
2703 * copy it to the real DC (the screen). This avoids
2704 * the problems with flickering.
2706 GetClientRect(hwndMain, &Rect);
2707 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
2708 Rect.bottom-Rect.top+1);
2709 oldBitmap = SelectObject(hdcmem, bufferBitmap);
2710 if (!appData.monoMode) {
2711 SelectPalette(hdcmem, hPal, FALSE);
2714 /* Create clips for dragging */
2716 if (dragInfo.from.x >= 0) {
2717 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
2718 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2720 if (dragInfo.start.x >= 0) {
2721 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
2722 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2724 if (dragInfo.pos.x >= 0) {
2725 x = dragInfo.pos.x - squareSize / 2;
2726 y = dragInfo.pos.y - squareSize / 2;
2727 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2729 if (dragInfo.lastpos.x >= 0) {
2730 x = dragInfo.lastpos.x - squareSize / 2;
2731 y = dragInfo.lastpos.y - squareSize / 2;
2732 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2736 /* If dragging is in progress, we temporarely remove the piece */
2737 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
2738 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
2739 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
2742 /* Are we animating a move?
2744 * - remove the piece from the board (temporarely)
2745 * - calculate the clipping region
2748 if (animInfo.piece != EmptySquare) {
2749 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
2750 x = boardRect.left + animInfo.lastpos.x;
2751 y = boardRect.top + animInfo.lastpos.y;
2752 x2 = boardRect.left + animInfo.pos.x;
2753 y2 = boardRect.top + animInfo.pos.y;
2754 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
2755 /* Slight kludge. The real problem is that after AnimateMove is
2756 done, the position on the screen does not match lastDrawn.
2757 This currently causes trouble only on e.p. captures in
2758 atomic, where the piece moves to an empty square and then
2759 explodes. The old and new positions both had an empty square
2760 at the destination, but animation has drawn a piece there and
2761 we have to remember to erase it. */
2762 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
2766 /* No clips? Make sure we have fullrepaint set to TRUE */
2770 /* Set clipping on the memory DC */
2772 SelectClipRgn(hdcmem, clips[0]);
2773 for (x = 1; x < num_clips; x++) {
2774 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
2775 abort(); // this should never ever happen!
2779 /* Do all the drawing to the memory DC */
2780 DrawGridOnDC(hdcmem);
2781 DrawHighlightsOnDC(hdcmem);
2782 DrawBoardOnDC(hdcmem, board, tmphdc);
2783 DrawCoordsOnDC(hdcmem);
2785 /* Put the dragged piece back into place and draw it */
2786 if (dragged_piece != EmptySquare) {
2787 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
2788 x = dragInfo.pos.x - squareSize / 2;
2789 y = dragInfo.pos.y - squareSize / 2;
2790 DrawPieceOnDC(hdcmem, dragged_piece,
2791 ((int) dragged_piece < (int) BlackPawn),
2792 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
2795 /* Put the animated piece back into place and draw it */
2796 if (animInfo.piece != EmptySquare) {
2797 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
2798 x = boardRect.left + animInfo.pos.x;
2799 y = boardRect.top + animInfo.pos.y;
2800 DrawPieceOnDC(hdcmem, animInfo.piece,
2801 ((int) animInfo.piece < (int) BlackPawn),
2802 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
2805 /* Release the bufferBitmap by selecting in the old bitmap
2806 * and delete the memory DC
2808 SelectObject(hdcmem, oldBitmap);
2811 /* Set clipping on the target DC */
2813 SelectClipRgn(hdc, clips[0]);
2814 for (x = 1; x < num_clips; x++) {
2815 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
2816 abort(); // this should never ever happen!
2820 /* Copy the new bitmap onto the screen in one go.
2821 * This way we avoid any flickering
2823 oldBitmap = SelectObject(tmphdc, bufferBitmap);
2824 BitBlt(hdc, boardRect.left, boardRect.top,
2825 boardRect.right - boardRect.left,
2826 boardRect.bottom - boardRect.top,
2827 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
2828 SelectObject(tmphdc, oldBitmap);
2830 /* Massive cleanup */
2831 for (x = 0; x < num_clips; x++)
2832 DeleteObject(clips[x]);
2835 DeleteObject(bufferBitmap);
2838 ReleaseDC(hwndMain, hdc);
2840 if (lastDrawnFlipView != flipView) {
2842 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
2844 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
2847 CopyBoard(lastDrawn, board);
2848 lastDrawnHighlight = highlightInfo;
2849 lastDrawnPremove = premoveHighlightInfo;
2850 lastDrawnFlipView = flipView;
2855 /*---------------------------------------------------------------------------*\
2856 | CLIENT PAINT PROCEDURE
2857 | This is the main event-handler for the WM_PAINT message.
2859 \*---------------------------------------------------------------------------*/
2861 PaintProc(HWND hwnd)
2867 if(hdc = BeginPaint(hwnd, &ps)) {
2868 if (IsIconic(hwnd)) {
2869 DrawIcon(hdc, 2, 2, iconCurrent);
2871 if (!appData.monoMode) {
2872 SelectPalette(hdc, hPal, FALSE);
2873 RealizePalette(hdc);
2875 HDCDrawPosition(hdc, 1, NULL);
2877 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2878 ExtTextOut(hdc, messageRect.left, messageRect.top,
2879 ETO_CLIPPED|ETO_OPAQUE,
2880 &messageRect, messageText, strlen(messageText), NULL);
2881 SelectObject(hdc, oldFont);
2882 DisplayBothClocks();
2892 * If the user selects on a border boundary, return -1; if off the board,
2893 * return -2. Otherwise map the event coordinate to the square.
2894 * The offset boardRect.left or boardRect.top must already have been
2895 * subtracted from x.
2898 EventToSquare(int x)
2905 if ((x % (squareSize + lineGap)) >= squareSize)
2907 x /= (squareSize + lineGap);
2908 if (x >= BOARD_SIZE)
2919 DropEnable dropEnables[] = {
2920 { 'P', DP_Pawn, "Pawn" },
2921 { 'N', DP_Knight, "Knight" },
2922 { 'B', DP_Bishop, "Bishop" },
2923 { 'R', DP_Rook, "Rook" },
2924 { 'Q', DP_Queen, "Queen" },
2928 SetupDropMenu(HMENU hmenu)
2930 int i, count, enable;
2932 extern char white_holding[], black_holding[];
2935 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
2936 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2937 dropEnables[i].piece);
2939 while (p && *p++ == dropEnables[i].piece) count++;
2940 sprintf(item, "%s %d", dropEnables[i].name, count);
2941 enable = count > 0 || !appData.testLegality
2942 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2943 && !appData.icsActive);
2944 ModifyMenu(hmenu, dropEnables[i].command,
2945 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
2946 dropEnables[i].command, item);
2950 static int fromX = -1, fromY = -1, toX, toY;
2952 /* Event handler for mouse messages */
2954 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2958 static int recursive = 0;
2960 BOOLEAN saveAnimate;
2961 static BOOLEAN sameAgain = FALSE;
2964 if (message == WM_MBUTTONUP) {
2965 /* Hideous kludge to fool TrackPopupMenu into paying attention
2966 to the middle button: we simulate pressing the left button too!
2968 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
2969 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
2975 pt.x = LOWORD(lParam);
2976 pt.y = HIWORD(lParam);
2977 x = EventToSquare(pt.x - boardRect.left);
2978 y = EventToSquare(pt.y - boardRect.top);
2979 if (!flipView && y >= 0) {
2980 y = BOARD_SIZE - 1 - y;
2982 if (flipView && x >= 0) {
2983 x = BOARD_SIZE - 1 - x;
2987 case WM_LBUTTONDOWN:
2991 /* Downclick vertically off board; check if on clock */
2992 if (PtInRect((LPRECT) &whiteRect, pt)) {
2993 if (gameMode == EditPosition) {
2994 SetWhiteToPlayEvent();
2995 } else if (gameMode == IcsPlayingBlack ||
2996 gameMode == MachinePlaysWhite) {
2999 } else if (PtInRect((LPRECT) &blackRect, pt)) {
3000 if (gameMode == EditPosition) {
3001 SetBlackToPlayEvent();
3002 } else if (gameMode == IcsPlayingWhite ||
3003 gameMode == MachinePlaysBlack) {
3007 if (!appData.highlightLastMove) {
3009 DrawPosition(FALSE, NULL);
3012 dragInfo.start.x = dragInfo.start.y = -1;
3013 dragInfo.from = dragInfo.start;
3015 } else if (x < 0 || y < 0) {
3017 } else if (fromX == x && fromY == y) {
3018 /* Downclick on same square again */
3020 DrawPosition(FALSE, NULL);
3022 } else if (fromX != -1) {
3023 /* Downclick on different square */
3024 ChessSquare pdown, pup;
3025 pdown = boards[currentMove][fromY][fromX];
3026 pup = boards[currentMove][y][x];
3027 if (gameMode == EditPosition ||
3028 !((WhitePawn <= pdown && pdown <= WhiteKing &&
3029 WhitePawn <= pup && pup <= WhiteKing) ||
3030 (BlackPawn <= pdown && pdown <= BlackKing &&
3031 BlackPawn <= pup && pup <= BlackKing))) {
3032 /* EditPosition, empty square, or different color piece;
3033 click-click move is possible */
3036 if (IsPromotion(fromX, fromY, toX, toY)) {
3037 if (appData.alwaysPromoteToQueen) {
3038 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3039 if (!appData.highlightLastMove) {
3041 DrawPosition(FALSE, NULL);
3044 SetHighlights(fromX, fromY, toX, toY);
3045 DrawPosition(FALSE, NULL);
3046 PromotionPopup(hwnd);
3048 } else { /* not a promotion */
3049 if (appData.animate || appData.highlightLastMove) {
3050 SetHighlights(fromX, fromY, toX, toY);
3054 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3055 if (appData.animate && !appData.highlightLastMove) {
3057 DrawPosition(FALSE, NULL);
3060 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3065 DrawPosition(FALSE, NULL);
3067 /* First downclick, or restart on a square with same color piece */
3068 if (!frozen && OKToStartUserMove(x, y)) {
3071 dragInfo.lastpos = pt;
3072 dragInfo.from.x = fromX;
3073 dragInfo.from.y = fromY;
3074 dragInfo.start = dragInfo.from;
3075 SetCapture(hwndMain);
3078 dragInfo.start.x = dragInfo.start.y = -1;
3079 dragInfo.from = dragInfo.start;
3085 if (fromX == -1) break;
3086 if (x == fromX && y == fromY) {
3087 dragInfo.from.x = dragInfo.from.y = -1;
3088 /* Upclick on same square */
3090 /* Clicked same square twice: abort click-click move */
3093 ClearPremoveHighlights();
3095 /* First square clicked: start click-click move */
3096 SetHighlights(fromX, fromY, -1, -1);
3098 DrawPosition(FALSE, NULL);
3099 } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {
3100 /* Errant click; ignore */
3103 /* Finish drag move */
3104 dragInfo.from.x = dragInfo.from.y = -1;
3107 saveAnimate = appData.animate; /* sorry, Hawk :) */
3108 appData.animate = appData.animate && !appData.animateDragging;
3109 if (IsPromotion(fromX, fromY, toX, toY)) {
3110 if (appData.alwaysPromoteToQueen) {
3111 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3113 DrawPosition(FALSE, NULL);
3114 PromotionPopup(hwnd);
3117 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3119 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3120 appData.animate = saveAnimate;
3122 if (appData.highlightDragging && !appData.highlightLastMove) {
3125 if (appData.animate || appData.animateDragging ||
3126 appData.highlightDragging || gotPremove) {
3127 DrawPosition(FALSE, NULL);
3130 dragInfo.start.x = dragInfo.start.y = -1;
3131 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
3135 if ((appData.animateDragging || appData.highlightDragging)
3136 && (wParam & MK_LBUTTON)
3137 && dragInfo.from.x >= 0) {
3138 if (appData.animateDragging) {
3141 if (appData.highlightDragging) {
3142 SetHighlights(fromX, fromY, x, y);
3144 DrawPosition(FALSE, NULL);
3145 dragInfo.lastpos = dragInfo.pos;
3150 signed short u = HIWORD(wParam);
3151 fprintf(debugFP, "mouse\n");
3153 /* Example: if the mouse wheel is brought forward one click, u is 120. Two clicks, its 240.
3154 if the mouse wheel is brought back one click, its -120, two clicks, -240, etc. */
3156 if (u && !(u%WHEEL_DELTA)) {
3171 case WM_MBUTTONDOWN:
3172 case WM_RBUTTONDOWN:
3176 dragInfo.pos.x = dragInfo.pos.y = -1;
3177 dragInfo.start.x = dragInfo.start.y = -1;
3178 dragInfo.from = dragInfo.start;
3179 dragInfo.lastpos = dragInfo.pos;
3180 if (appData.highlightDragging) {
3183 DrawPosition(TRUE, NULL);
3188 if (x < 0 || y < 0) break;
3191 if (message == WM_MBUTTONDOWN) {
3192 buttonCount = 3; /* even if system didn't think so */
3193 if (wParam & MK_SHIFT)
3194 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3196 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3197 } else { /* message == WM_RBUTTONDOWN */
3199 if (buttonCount == 3) {
3200 if (wParam & MK_SHIFT)
3201 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3203 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3205 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3208 /* Just have one menu, on the right button. Windows users don't
3209 think to try the middle one, and sometimes other software steals
3210 it, or it doesn't really exist. */
3211 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3215 case IcsPlayingWhite:
3216 case IcsPlayingBlack:
3218 case MachinePlaysWhite:
3219 case MachinePlaysBlack:
3220 if (appData.testLegality &&
3221 gameInfo.variant != VariantBughouse &&
3222 gameInfo.variant != VariantCrazyhouse) break;
3223 if (x < 0 || y < 0) break;
3226 hmenu = LoadMenu(hInst, "DropPieceMenu");
3227 SetupDropMenu(hmenu);
3228 MenuPopup(hwnd, pt, hmenu, -1);
3239 /* Preprocess messages for buttons in main window */
3241 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3243 int id = GetWindowLong(hwnd, GWL_ID);
3246 for (i=0; i<N_BUTTONS; i++) {
3247 if (buttonDesc[i].id == id) break;
3249 if (i == N_BUTTONS) return 0;
3255 dir = (wParam == VK_LEFT) ? -1 : 1;
3256 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
3263 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
3266 if (appData.icsActive) {
3267 if (GetKeyState(VK_SHIFT) < 0) {
3269 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3270 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3274 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3275 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3282 if (appData.icsActive) {
3283 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3284 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3286 SendMessage(h, WM_CHAR, wParam, lParam);
3288 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
3289 PopUpMoveDialog((char)wParam);
3295 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
3298 /* Process messages for Promotion dialog box */
3300 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
3305 case WM_INITDIALOG: /* message: initialize dialog box */
3306 /* Center the dialog over the application window */
3307 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
3308 ShowWindow(GetDlgItem(hDlg, PB_King),
3309 (!appData.testLegality || gameInfo.variant == VariantSuicide) ?
3313 case WM_COMMAND: /* message: received a command */
3314 switch (LOWORD(wParam)) {
3316 EndDialog(hDlg, TRUE); /* Exit the dialog */
3318 DrawPosition(FALSE, NULL);
3338 EndDialog(hDlg, TRUE); /* Exit the dialog */
3339 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3340 if (!appData.highlightLastMove) {
3342 DrawPosition(FALSE, NULL);
3349 /* Pop up promotion dialog */
3351 PromotionPopup(HWND hwnd)
3355 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
3356 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
3357 hwnd, (DLGPROC)lpProc);
3358 FreeProcInstance(lpProc);
3361 /* Toggle ShowThinking */
3363 ToggleShowThinking()
3365 ShowThinkingEvent(!appData.showThinking);
3369 LoadGameDialog(HWND hwnd, char* title)
3373 char fileTitle[MSG_SIZ];
3374 f = OpenFileDialog(hwnd, FALSE, "",
3375 appData.oldSaveStyle ? "gam" : "pgn",
3377 title, &number, fileTitle, NULL);
3379 cmailMsgLoaded = FALSE;
3381 int error = GameListBuild(f);
3383 DisplayError("Cannot build game list", error);
3384 } else if (!ListEmpty(&gameList) &&
3385 ((ListGame *) gameList.tailPred)->number > 1) {
3386 GameListPopUp(f, fileTitle);
3392 LoadGame(f, number, fileTitle, FALSE);
3397 ChangedConsoleFont()
3400 CHARRANGE tmpsel, sel;
3401 MyFont *f = font[boardSize][CONSOLE_FONT];
3402 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
3403 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3406 cfmt.cbSize = sizeof(CHARFORMAT);
3407 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
3408 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
3409 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
3410 * size. This was undocumented in the version of MSVC++ that I had
3411 * when I wrote the code, but is apparently documented now.
3413 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
3414 cfmt.bCharSet = f->lf.lfCharSet;
3415 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
3416 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3417 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3418 /* Why are the following seemingly needed too? */
3419 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3420 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3421 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
3423 tmpsel.cpMax = -1; /*999999?*/
3424 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
3425 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
3426 /* Trying putting this here too. It still seems to tickle a RichEdit
3427 * bug: sometimes RichEdit indents the first line of a paragraph too.
3429 paraf.cbSize = sizeof(paraf);
3430 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
3431 paraf.dxStartIndent = 0;
3432 paraf.dxOffset = WRAP_INDENT;
3433 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
3434 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
3437 /*---------------------------------------------------------------------------*\
3439 * Window Proc for main window
3441 \*---------------------------------------------------------------------------*/
3443 /* Process messages for main window, etc. */
3445 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3452 char fileTitle[MSG_SIZ];
3456 case WM_PAINT: /* message: repaint portion of window */
3461 if (IsIconic(hwnd)) {
3462 /* Cheat; change the message */
3463 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
3465 return (DefWindowProc(hwnd, message, wParam, lParam));
3469 case WM_LBUTTONDOWN:
3470 case WM_MBUTTONDOWN:
3471 case WM_RBUTTONDOWN:
3477 MouseEvent(hwnd, message, wParam, lParam);
3482 if (appData.icsActive) {
3483 if (wParam == '\t') {
3484 if (GetKeyState(VK_SHIFT) < 0) {
3486 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3487 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3491 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3492 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3496 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3497 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3499 SendMessage(h, message, wParam, lParam);
3501 } else if (isalpha((char)wParam) || isdigit((char)wParam)) {
3502 PopUpMoveDialog((char)wParam);
3506 case WM_PALETTECHANGED:
3507 if (hwnd != (HWND)wParam && !appData.monoMode) {
3509 HDC hdc = GetDC(hwndMain);
3510 SelectPalette(hdc, hPal, TRUE);
3511 nnew = RealizePalette(hdc);
3513 paletteChanged = TRUE;
3517 InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/
3520 ReleaseDC(hwnd, hdc);
3524 case WM_QUERYNEWPALETTE:
3525 if (!appData.monoMode /*&& paletteChanged*/) {
3527 HDC hdc = GetDC(hwndMain);
3528 paletteChanged = FALSE;
3529 SelectPalette(hdc, hPal, FALSE);
3530 nnew = RealizePalette(hdc);
3532 InvalidateRect(hwnd, &boardRect, FALSE);
3534 ReleaseDC(hwnd, hdc);
3539 case WM_COMMAND: /* message: command from application menu */
3540 wmId = LOWORD(wParam);
3541 wmEvent = HIWORD(wParam);
3547 /*AnalysisPopDown(); */
3551 LoadGameDialog(hwnd, "Load Game from File");
3554 case IDM_LoadNextGame:
3558 case IDM_LoadPrevGame:
3562 case IDM_ReloadGame:
3566 case IDM_LoadPosition:
3567 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
3571 f = OpenFileDialog(hwnd, FALSE, "",
3572 appData.oldSaveStyle ? "pos" : "fen",
3574 "Load Position from File", &number, fileTitle, NULL);
3576 LoadPosition(f, number, fileTitle);
3580 case IDM_LoadNextPosition:
3584 case IDM_LoadPrevPosition:
3588 case IDM_ReloadPosition:
3593 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
3594 f = OpenFileDialog(hwnd, TRUE, defName,
3595 appData.oldSaveStyle ? "gam" : "pgn",
3597 "Save Game to File", NULL, fileTitle, NULL);
3603 case IDM_SavePosition:
3604 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
3605 f = OpenFileDialog(hwnd, TRUE, defName,
3606 appData.oldSaveStyle ? "pos" : "fen",
3608 "Save Position to File", NULL, fileTitle, NULL);
3610 SavePosition(f, 0, "");
3615 CopyGameToClipboard();
3619 PasteGameFromClipboard();
3622 case IDM_CopyPosition:
3623 CopyFENToClipboard();
3626 case IDM_PastePosition:
3627 PasteFENFromClipboard();
3634 case IDM_ReloadCMailMsg:
3636 ReloadCmailMsgEvent(FALSE);
3640 ShowWindow(hwnd, SW_MINIMIZE);
3643 case IDM_focus_EngineRoom:
3644 SetFocus(analysisDialog);
3655 case IDM_MachineWhite:
3656 MachineWhiteEvent();
3658 * refresh the tags dialog only if it's visible
3660 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
3662 tags = PGNTags(&gameInfo);
3663 TagsPopUp(tags, CmailMsg());
3668 case IDM_MachineBlack:
3669 MachineBlackEvent();
3671 * refresh the tags dialog only if it's visible
3673 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
3675 tags = PGNTags(&gameInfo);
3676 TagsPopUp(tags, CmailMsg());
3681 case IDM_TwoMachines:
3684 * refresh the tags dialog only if it's visible
3686 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
3688 tags = PGNTags(&gameInfo);
3689 TagsPopUp(tags, CmailMsg());
3694 case IDM_AnalysisMode:
3695 if (!first.analysisSupport) {
3697 sprintf(buf, "%s does not support analysis", first.tidy);
3698 DisplayError(buf, 0);
3700 if (!appData.showThinking) ToggleShowThinking();
3705 case IDM_AnalyzeFile:
3706 if (!first.analysisSupport) {
3708 sprintf(buf, "%s does not support analysis", first.tidy);
3709 DisplayError(buf, 0);
3711 if (!appData.showThinking) ToggleShowThinking();
3713 LoadGameDialog(hwnd, "Analyze Game from File");
3714 AnalysisPeriodicEvent(1);
3726 case IDM_EditPosition:
3727 EditPositionEvent();
3734 case IDM_ShowGameList:
3742 case IDM_EditComment:
3743 if (commentDialogUp && editComment) {
3786 case IDM_StopObserving:
3787 StopObservingEvent();
3790 case IDM_StopExamining:
3791 StopExaminingEvent();
3794 case IDM_TypeInMove:
3795 PopUpMoveDialog('\000');
3822 case IDM_TruncateGame:
3823 TruncateGameEvent();
3830 case IDM_RetractMove:
3835 flipView = !flipView;
3836 DrawPosition(FALSE, NULL);
3839 case IDM_GeneralOptions:
3840 GeneralOptionsPopup(hwnd);
3843 case IDM_BoardOptions:
3844 BoardOptionsPopup(hwnd);
3847 case IDM_IcsOptions:
3848 IcsOptionsPopup(hwnd);
3852 icsZippyDrawPopUp(hwnd);
3856 FontsOptionsPopup(hwnd);
3860 SoundOptionsPopup(hwnd);
3864 CommPortOptionsPopup(hwnd);
3867 case IDM_LoadOptions:
3868 LoadOptionsPopup(hwnd);
3871 case IDM_SaveOptions:
3872 SaveOptionsPopup(hwnd);
3875 case IDM_TimeControl:
3876 TimeControlOptionsPopup(hwnd);
3879 case IDM_SaveSettings:
3880 SaveSettings(settingsFileName);
3883 case IDM_SaveSettingsOnExit:
3884 saveSettingsOnExit = !saveSettingsOnExit;
3885 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
3886 MF_BYCOMMAND|(saveSettingsOnExit ?
3887 MF_CHECKED : MF_UNCHECKED));
3903 appData.debugMode = !appData.debugMode;
3904 if (appData.debugMode) {
3906 GetCurrentDirectory(MSG_SIZ, dir);
3907 SetCurrentDirectory(installDir);
3908 debugFP = fopen("WinBoard.debug", "w");
3909 SetCurrentDirectory(dir);
3910 setbuf(debugFP, NULL);
3917 case IDM_HELPCONTENTS:
3918 if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
3919 MessageBox (GetFocus(),
3920 "Unable to activate help",
3921 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3925 case IDM_HELPSEARCH:
3926 if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
3927 MessageBox (GetFocus(),
3928 "Unable to activate help",
3929 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3934 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
3935 MessageBox (GetFocus(),
3936 "Unable to activate help",
3937 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3942 lpProc = MakeProcInstance((FARPROC)About, hInst);
3944 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
3945 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
3946 FreeProcInstance(lpProc);
3949 case IDM_DirectCommand1:
3950 AskQuestionEvent("Direct Command",
3951 "Send to chess program:", "", "1");
3953 case IDM_DirectCommand2:
3954 AskQuestionEvent("Direct Command",
3955 "Send to second chess program:", "", "2");
3959 EditPositionMenuEvent(WhitePawn, fromX, fromY);
3963 case EP_WhiteKnight:
3964 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
3968 case EP_WhiteBishop:
3969 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
3974 EditPositionMenuEvent(WhiteRook, fromX, fromY);
3979 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
3984 EditPositionMenuEvent(WhiteKing, fromX, fromY);
3989 EditPositionMenuEvent(BlackPawn, fromX, fromY);
3993 case EP_BlackKnight:
3994 EditPositionMenuEvent(BlackKnight, fromX, fromY);
3998 case EP_BlackBishop:
3999 EditPositionMenuEvent(BlackBishop, fromX, fromY);
4004 EditPositionMenuEvent(BlackRook, fromX, fromY);
4009 EditPositionMenuEvent(BlackQueen, fromX, fromY);
4014 EditPositionMenuEvent(BlackKing, fromX, fromY);
4018 case EP_EmptySquare:
4019 EditPositionMenuEvent(EmptySquare, fromX, fromY);
4024 EditPositionMenuEvent(ClearBoard, fromX, fromY);
4029 EditPositionMenuEvent(WhitePlay, fromX, fromY);
4034 EditPositionMenuEvent(BlackPlay, fromX, fromY);
4039 DropMenuEvent(WhitePawn, fromX, fromY);
4044 DropMenuEvent(WhiteKnight, fromX, fromY);
4049 DropMenuEvent(WhiteBishop, fromX, fromY);
4054 DropMenuEvent(WhiteRook, fromX, fromY);
4059 DropMenuEvent(WhiteQueen, fromX, fromY);
4064 return (DefWindowProc(hwnd, message, wParam, lParam));
4070 case CLOCK_TIMER_ID:
4071 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
4072 clockTimerEvent = 0;
4073 DecrementClocks(); /* call into back end */
4075 case LOAD_GAME_TIMER_ID:
4076 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
4077 loadGameTimerEvent = 0;
4078 AutoPlayGameLoop(); /* call into back end */
4080 case ANALYSIS_TIMER_ID:
4081 /* dirty hack without stat line and support */
4082 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
4083 appData.icsAnalyzeWindow || appData.AnalysisWindow) &&
4084 appData.periodicUpdates) {
4085 AnalysisPeriodicEvent(0);
4087 KillTimer(hwnd, analysisTimerEvent);
4088 analysisTimerEvent = 0;
4091 case DELAYED_TIMER_ID:
4092 KillTimer(hwnd, delayedTimerEvent);
4093 delayedTimerEvent = 0;
4094 delayedTimerCallback();
4100 InputEvent(hwnd, message, wParam, lParam);
4103 case WM_ENTERSIZEMOVE:
4104 if (hwnd == hwndMain) {
4111 if (hwnd == hwndMain) {
4112 lastSizing = wParam;
4116 case WM_EXITSIZEMOVE:
4117 if (hwnd == hwndMain) {
4119 doingSizing = FALSE;
4120 InvalidateRect(hwnd, &boardRect, FALSE);
4121 GetClientRect(hwnd, &client);
4122 ResizeBoard(client.right, client.bottom, lastSizing);
4127 case WM_DESTROY: /* message: window being destroyed */
4132 if (hwnd == hwndMain) {
4137 default: /* Passes it on if unprocessed */
4138 return (DefWindowProc(hwnd, message, wParam, lParam));
4143 /*---------------------------------------------------------------------------*\
4145 * Misc utility routines
4147 \*---------------------------------------------------------------------------*/
4150 * returns TRUE if user selects a different color, FALSE otherwise
4154 ChangeColor(HWND hwnd, COLORREF *which)
4156 static BOOL firstTime = TRUE;
4157 static DWORD customColors[16];
4164 /* Make initial colors in use available as custom colors */
4165 /* Should we put the compiled-in defaults here instead? */
4167 customColors[i++] = lightSquareColor & 0xffffff;
4168 customColors[i++] = darkSquareColor & 0xffffff;
4169 customColors[i++] = whitePieceColor & 0xffffff;
4170 customColors[i++] = blackPieceColor & 0xffffff;
4171 customColors[i++] = highlightSquareColor & 0xffffff;
4172 customColors[i++] = premoveHighlightColor & 0xffffff;
4174 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
4175 customColors[i++] = textAttribs[ccl].color;
4177 while (i < 16) customColors[i++] = RGB(255, 255, 255);
4181 cc.lStructSize = sizeof(cc);
4182 cc.hwndOwner = hwnd;
4183 cc.hInstance = NULL;
4184 cc.rgbResult = (DWORD) (*which & 0xffffff);
4185 cc.lpCustColors = (LPDWORD) customColors;
4186 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
4188 if (!ChooseColor(&cc)) return FALSE;
4190 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
4191 if (newcolor == *which) return FALSE;
4196 InitDrawingColors();
4197 InvalidateRect(hwnd, &boardRect, FALSE);
4202 MyLoadSound(MySound *ms)
4208 if (ms->data) free(ms->data);
4211 switch (ms->name[0]) {
4217 /* System sound from Control Panel. Don't preload here. */
4221 if (ms->name[1] == NULLCHAR) {
4222 /* "!" alone = silence */
4225 /* Builtin wave resource. Error if not found. */
4226 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
4227 if (h == NULL) break;
4228 ms->data = (void *)LoadResource(hInst, h);
4229 if (h == NULL) break;
4234 /* .wav file. Error if not found. */
4235 f = fopen(ms->name, "rb");
4236 if (f == NULL) break;
4237 if (fstat(fileno(f), &st) < 0) break;
4238 ms->data = malloc(st.st_size);
4239 if (fread(ms->data, st.st_size, 1, f) < 1) break;
4246 sprintf(buf, "Error loading sound %s", ms->name);
4247 DisplayError(buf, GetLastError());
4253 MyPlaySound(MySound *ms)
4256 switch (ms->name[0]) {
4262 /* System sound from Control Panel (deprecated feature).
4263 "$" alone or an unset sound name gets default beep (still in use). */
4265 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
4267 if (!ok) ok = MessageBeep(MB_OK);
4270 /* Builtin wave resource, or "!" alone for silence */
4272 if (ms->data == NULL) return FALSE;
4273 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4279 /* .wav file. Error if not found. */
4280 if (ms->data == NULL) return FALSE;
4281 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4284 /* Don't print an error: this can happen innocently if the sound driver
4285 is busy; for instance, if another instance of WinBoard is playing
4286 a sound at about the same time. */
4290 sprintf(buf, "Error playing sound %s", ms->name);
4291 DisplayError(buf, GetLastError());
4299 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4303 static UINT *number; /* gross that this is static */
4306 case WM_INITDIALOG: /* message: initialize dialog box */
4307 /* Center the dialog over the application window */
4308 ofn = (OPENFILENAME *) lParam;
4309 if (ofn->Flags & OFN_ENABLETEMPLATE) {
4310 number = (UINT *) ofn->lCustData;
4311 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
4315 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4316 return FALSE; /* Allow for further processing */
4319 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
4320 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
4322 return FALSE; /* Allow for further processing */
4328 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
4330 static UINT *number;
4331 OPENFILENAME *ofname;
4335 ofname = (OPENFILENAME *)lParam;
4336 number = (UINT *)(ofname->lCustData);
4339 ofnot = (OFNOTIFY *)lParam;
4340 if (ofnot->hdr.code == CDN_FILEOK) {
4341 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
4350 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,
4351 char *nameFilt, char *dlgTitle, UINT *number,
4352 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
4354 OPENFILENAME openFileName;
4358 if (fileName == NULL) fileName = buf1;
4359 if (defName == NULL) {
4360 strcpy(fileName, "*.");
4361 strcat(fileName, defExt);
4363 strcpy(fileName, defName);
4365 if (fileTitle) strcpy(fileTitle, "");
4366 if (number) *number = 0;
4368 openFileName.lStructSize = sizeof(OPENFILENAME);
4369 openFileName.hwndOwner = hwnd;
4370 openFileName.hInstance = (HANDLE) hInst;
4371 openFileName.lpstrFilter = nameFilt;
4372 openFileName.lpstrCustomFilter = (LPSTR) NULL;
4373 openFileName.nMaxCustFilter = 0L;
4374 openFileName.nFilterIndex = 1L;
4375 openFileName.lpstrFile = fileName;
4376 openFileName.nMaxFile = MSG_SIZ;
4377 openFileName.lpstrFileTitle = fileTitle;
4378 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
4379 openFileName.lpstrInitialDir = NULL;
4380 openFileName.lpstrTitle = dlgTitle;
4381 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
4382 | (write ? 0 : OFN_FILEMUSTEXIST)
4383 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
4384 | (oldDialog ? 0 : OFN_EXPLORER);
4385 openFileName.nFileOffset = 0;
4386 openFileName.nFileExtension = 0;
4387 openFileName.lpstrDefExt = defExt;
4388 openFileName.lCustData = (LONG) number;
4389 openFileName.lpfnHook = oldDialog ?
4390 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
4391 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
4393 if (write ? GetSaveFileName(&openFileName) :
4394 GetOpenFileName(&openFileName)) {
4396 f = fopen(openFileName.lpstrFile, write ? "a" : "r");
4398 MessageBox(hwnd, "File open failed", NULL,
4399 MB_OK|MB_ICONEXCLAMATION);
4403 int err = CommDlgExtendedError();
4404 if (err != 0) DisplayError("Internal error in file dialog box", err);
4413 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
4415 HMENU hmenuTrackPopup; /* floating pop-up menu */
4418 * Get the first pop-up menu in the menu template. This is the
4419 * menu that TrackPopupMenu displays.
4421 hmenuTrackPopup = GetSubMenu(hmenu, 0);
4423 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
4426 * TrackPopup uses screen coordinates, so convert the
4427 * coordinates of the mouse click to screen coordinates.
4429 ClientToScreen(hwnd, (LPPOINT) &pt);
4431 /* Draw and track the floating pop-up menu. */
4432 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
4433 pt.x, pt.y, 0, hwnd, NULL);
4435 /* Destroy the menu.*/
4441 int sizeX, sizeY, newSizeX, newSizeY;
4443 } ResizeEditPlusButtonsClosure;
4446 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
4448 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
4452 if (hChild == cl->hText) return TRUE;
4453 GetWindowRect(hChild, &rect); /* gives screen coords */
4454 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
4455 pt.y = rect.top + cl->newSizeY - cl->sizeY;
4456 ScreenToClient(cl->hDlg, &pt);
4457 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
4458 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
4462 /* Resize a dialog that has a (rich) edit field filling most of
4463 the top, with a row of buttons below */
4465 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
4468 int newTextHeight, newTextWidth;
4469 ResizeEditPlusButtonsClosure cl;
4471 /*if (IsIconic(hDlg)) return;*/
4472 if (newSizeX == sizeX && newSizeY == sizeY) return;
4474 cl.hdwp = BeginDeferWindowPos(8);
4476 GetWindowRect(hText, &rectText); /* gives screen coords */
4477 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
4478 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
4479 if (newTextHeight < 0) {
4480 newSizeY += -newTextHeight;
4483 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
4484 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
4490 cl.newSizeX = newSizeX;
4491 cl.newSizeY = newSizeY;
4492 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
4494 EndDeferWindowPos(cl.hdwp);
4497 /* Center one window over another */
4498 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
4500 RECT rChild, rParent;
4501 int wChild, hChild, wParent, hParent;
4502 int wScreen, hScreen, xNew, yNew;
4505 /* Get the Height and Width of the child window */
4506 GetWindowRect (hwndChild, &rChild);
4507 wChild = rChild.right - rChild.left;
4508 hChild = rChild.bottom - rChild.top;
4510 /* Get the Height and Width of the parent window */
4511 GetWindowRect (hwndParent, &rParent);
4512 wParent = rParent.right - rParent.left;
4513 hParent = rParent.bottom - rParent.top;
4515 /* Get the display limits */
4516 hdc = GetDC (hwndChild);
4517 wScreen = GetDeviceCaps (hdc, HORZRES);
4518 hScreen = GetDeviceCaps (hdc, VERTRES);
4519 ReleaseDC(hwndChild, hdc);
4521 /* Calculate new X position, then adjust for screen */
4522 xNew = rParent.left + ((wParent - wChild) /2);
4525 } else if ((xNew+wChild) > wScreen) {
4526 xNew = wScreen - wChild;
4529 /* Calculate new Y position, then adjust for screen */
4530 yNew = rParent.top + ((hParent - hChild) /2);
4533 } else if ((yNew+hChild) > hScreen) {
4534 yNew = hScreen - hChild;
4537 /* Set it, and return */
4538 return SetWindowPos (hwndChild, NULL,
4539 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4542 /*---------------------------------------------------------------------------*\
4544 * Startup Dialog functions
4546 \*---------------------------------------------------------------------------*/
4548 InitComboStrings(HANDLE hwndCombo, char **cd)
4550 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4552 while (*cd != NULL) {
4553 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
4559 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
4564 if (str[0] == '@') {
4565 FILE* f = fopen(str + 1, "r");
4567 DisplayFatalError(str + 1, errno, 2);
4570 len = fread(buf1, 1, sizeof(buf1)-1, f);
4572 buf1[len] = NULLCHAR;
4576 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4580 char *end = strchr(str, '\n');
4581 if (end == NULL) return;
4582 memcpy(buf, str, end - str);
4583 buf[end - str] = NULLCHAR;
4584 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
4590 SetStartupDialogEnables(HWND hDlg)
4592 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
4593 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4594 appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));
4595 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
4596 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
4597 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
4598 IsDlgButtonChecked(hDlg, OPT_ChessServer));
4599 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
4600 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
4601 EnableWindow(GetDlgItem(hDlg, IDOK),
4602 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4603 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
4604 IsDlgButtonChecked(hDlg, OPT_View));
4608 QuoteForFilename(char *filename)
4611 dquote = strchr(filename, '"') != NULL;
4612 space = strchr(filename, ' ') != NULL;
4613 if (dquote || space) {
4625 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
4630 InitComboStringsFromOption(hwndCombo, nthnames);
4631 q = QuoteForFilename(nthcp);
4632 sprintf(buf, "%s%s%s", q, nthcp, q);
4633 if (*nthdir != NULLCHAR) {
4634 q = QuoteForFilename(nthdir);
4635 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
4637 if (*nthcp == NULLCHAR) {
4638 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4639 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4640 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4641 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4646 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4654 /* Center the dialog */
4655 CenterWindow (hDlg, GetDesktopWindow());
4656 /* Initialize the dialog items */
4657 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
4658 appData.firstChessProgram, "fd", appData.firstDirectory,
4659 firstChessProgramNames);
4660 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
4661 appData.secondChessProgram, "sd", appData.secondDirectory,
4662 secondChessProgramNames);
4663 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
4664 InitComboStringsFromOption(hwndCombo, icsNames);
4665 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
4666 if (*appData.icsHelper != NULLCHAR) {
4667 char *q = QuoteForFilename(appData.icsHelper);
4668 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
4670 if (*appData.icsHost == NULLCHAR) {
4671 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4672 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
4673 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4674 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4675 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4678 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
4679 } else if (appData.icsActive) {
4680 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
4681 } else if (appData.noChessProgram) {
4682 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
4684 SetStartupDialogEnables(hDlg);
4688 switch (LOWORD(wParam)) {
4690 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
4691 strcpy(buf, "/fcp=");
4692 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4694 ParseArgs(StringGet, &p);
4695 strcpy(buf, "/scp=");
4696 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4698 ParseArgs(StringGet, &p);
4699 appData.noChessProgram = FALSE;
4700 appData.icsActive = FALSE;
4701 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
4702 strcpy(buf, "/ics /icshost=");
4703 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4705 ParseArgs(StringGet, &p);
4706 if (appData.zippyPlay) {
4707 strcpy(buf, "/fcp=");
4708 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4710 ParseArgs(StringGet, &p);
4712 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
4713 appData.noChessProgram = TRUE;
4714 appData.icsActive = FALSE;
4716 MessageBox(hDlg, "Choose an option, or cancel to exit",
4717 "Option Error", MB_OK|MB_ICONEXCLAMATION);
4720 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
4721 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
4723 ParseArgs(StringGet, &p);
4725 EndDialog(hDlg, TRUE);
4732 case IDM_HELPCONTENTS:
4733 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
4734 MessageBox (GetFocus(),
4735 "Unable to activate help",
4736 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
4741 SetStartupDialogEnables(hDlg);
4749 /*---------------------------------------------------------------------------*\
4751 * About box dialog functions
4753 \*---------------------------------------------------------------------------*/
4755 /* Process messages for "About" dialog box */
4757 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4760 case WM_INITDIALOG: /* message: initialize dialog box */
4761 /* Center the dialog over the application window */
4762 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4763 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
4766 case WM_COMMAND: /* message: received a command */
4767 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
4768 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
4769 EndDialog(hDlg, TRUE); /* Exit the dialog */
4777 /*---------------------------------------------------------------------------*\
4779 * Comment Dialog functions
4781 \*---------------------------------------------------------------------------*/
4784 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4786 static HANDLE hwndText = NULL;
4787 int len, newSizeX, newSizeY, flags;
4788 static int sizeX, sizeY;
4794 case WM_INITDIALOG: /* message: initialize dialog box */
4795 /* Initialize the dialog items */
4796 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4797 SetDlgItemText(hDlg, OPT_CommentText, commentText);
4798 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
4799 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
4800 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
4801 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
4802 SetWindowText(hDlg, commentTitle);
4806 SetFocus(GetDlgItem(hDlg, IDOK));
4808 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
4809 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
4810 MAKELPARAM(FALSE, 0));
4811 /* Size and position the dialog */
4812 if (!commentDialog) {
4813 commentDialog = hDlg;
4814 flags = SWP_NOZORDER;
4815 GetClientRect(hDlg, &rect);
4817 sizeY = rect.bottom;
4818 if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&
4819 commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {
4821 EnsureOnScreen(&commentX, &commentY);
4822 wp.length = sizeof(WINDOWPLACEMENT);
4824 wp.showCmd = SW_SHOW;
4825 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
4826 wp.rcNormalPosition.left = commentX;
4827 wp.rcNormalPosition.right = commentX + commentW;
4828 wp.rcNormalPosition.top = commentY;
4829 wp.rcNormalPosition.bottom = commentY + commentH;
4830 SetWindowPlacement(hDlg, &wp);
4832 GetClientRect(hDlg, &rect);
4833 newSizeX = rect.right;
4834 newSizeY = rect.bottom;
4835 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
4836 newSizeX, newSizeY);
4843 case WM_COMMAND: /* message: received a command */
4844 switch (LOWORD(wParam)) {
4848 /* Read changed options from the dialog box */
4849 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4850 len = GetWindowTextLength(hwndText);
4851 str = (char *) malloc(len + 1);
4852 GetWindowText(hwndText, str, len + 1);
4861 ReplaceComment(commentIndex, str);
4868 case OPT_CancelComment:
4872 case OPT_ClearComment:
4873 SetDlgItemText(hDlg, OPT_CommentText, "");
4876 case OPT_EditComment:
4886 newSizeX = LOWORD(lParam);
4887 newSizeY = HIWORD(lParam);
4888 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
4893 case WM_GETMINMAXINFO:
4894 /* Prevent resizing window too small */
4895 mmi = (MINMAXINFO *) lParam;
4896 mmi->ptMinTrackSize.x = 100;
4897 mmi->ptMinTrackSize.y = 100;
4904 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
4909 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
4911 if (str == NULL) str = "";
4912 p = (char *) malloc(2 * strlen(str) + 2);
4915 if (*str == '\n') *q++ = '\r';
4919 if (commentText != NULL) free(commentText);
4921 commentIndex = index;
4922 commentTitle = title;
4926 if (commentDialog) {
4927 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
4928 if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);
4930 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
4931 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
4932 hwndMain, (DLGPROC)lpProc);
4933 FreeProcInstance(lpProc);
4935 commentDialogUp = TRUE;
4939 /*---------------------------------------------------------------------------*\
4941 * Type-in move dialog functions
4943 \*---------------------------------------------------------------------------*/
4946 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4951 int fromX, fromY, toX, toY;
4956 move[0] = (char) lParam;
4958 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
4959 hInput = GetDlgItem(hDlg, OPT_Move);
4960 SetWindowText(hInput, move);
4962 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
4966 switch (LOWORD(wParam)) {
4968 if (gameMode != EditGame && currentMove != forwardMostMove &&
4969 gameMode != Training) {
4970 DisplayMoveError("Displayed move is not current");
4972 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
4973 if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
4974 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
4975 if (gameMode != Training)
4976 forwardMostMove = currentMove;
4977 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4979 DisplayMoveError("Could not parse move");
4982 EndDialog(hDlg, TRUE);
4985 EndDialog(hDlg, FALSE);
4996 PopUpMoveDialog(char firstchar)
5000 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
5001 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
5002 gameMode == AnalyzeMode || gameMode == EditGame ||
5003 gameMode == EditPosition || gameMode == IcsExamining ||
5004 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
5005 gameMode == Training) {
5006 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
5007 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
5008 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
5009 FreeProcInstance(lpProc);
5013 /*---------------------------------------------------------------------------*\
5017 \*---------------------------------------------------------------------------*/
5019 /* Nonmodal error box */
5023 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
5024 if (errorDialog == NULL) return;
5025 DestroyWindow(errorDialog);
5030 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5037 GetWindowRect(hDlg, &rChild);
5038 SetWindowPos(hDlg, NULL, rChild.left,
5039 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
5040 0, 0, SWP_NOZORDER|SWP_NOSIZE);
5042 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
5043 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
5047 switch (LOWORD(wParam)) {
5050 if (errorDialog = hDlg) errorDialog = NULL;
5051 DestroyWindow(hDlg);
5062 /*---------------------------------------------------------------------------*\
5064 * Ics Interaction console functions
5066 \*---------------------------------------------------------------------------*/
5068 #define HISTORY_SIZE 64
5069 static char *history[HISTORY_SIZE];
5070 int histIn = 0, histP = 0;
5073 SaveInHistory(char *cmd)
5075 if (history[histIn] != NULL) {
5076 free(history[histIn]);
5077 history[histIn] = NULL;
5079 if (*cmd == NULLCHAR) return;
5080 history[histIn] = StrSave(cmd);
5081 histIn = (histIn + 1) % HISTORY_SIZE;
5082 if (history[histIn] != NULL) {
5083 free(history[histIn]);
5084 history[histIn] = NULL;
5090 PrevInHistory(char *cmd)
5093 if (histP == histIn) {
5094 if (history[histIn] != NULL) free(history[histIn]);
5095 history[histIn] = StrSave(cmd);
5097 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
5098 if (newhp == histIn || history[newhp] == NULL) return NULL;
5100 return history[histP];
5106 if (histP == histIn) return NULL;
5107 histP = (histP + 1) % HISTORY_SIZE;
5108 return history[histP];
5117 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
5118 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];
5121 ParseIcsTextMenu(char *icsTextMenuString)
5124 IcsTextMenuEntry *e = icsTextMenuEntry;
5125 char *p = icsTextMenuString;
5126 while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5129 if (e->command != NULL) {
5135 e = icsTextMenuEntry;
5136 while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5137 if (*p == ';' || *p == '\n') {
5138 e->item = strdup("-");
5141 } else if (*p == '-') {
5142 e->item = strdup("-");
5147 char *q, *r, *s, *t;
5150 if (q == NULL) break;
5152 r = strchr(q + 1, ',');
5153 if (r == NULL) break;
5155 s = strchr(r + 1, ',');
5156 if (s == NULL) break;
5159 t = strchr(s + 1, c);
5162 t = strchr(s + 1, c);
5164 if (t != NULL) *t = NULLCHAR;
5165 e->item = strdup(p);
5166 e->command = strdup(q + 1);
5167 e->getname = *(r + 1) != '0';
5168 e->immediate = *(s + 1) != '0';
5172 if (t == NULL) break;
5181 LoadIcsTextMenu(IcsTextMenuEntry *e)
5185 hmenu = LoadMenu(hInst, "TextMenu");
5186 h = GetSubMenu(hmenu, 0);
5188 if (strcmp(e->item, "-") == 0) {
5189 AppendMenu(h, MF_SEPARATOR, 0, 0);
5191 if (e->item[0] == '|') {
5192 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
5193 IDM_CommandX + i, &e->item[1]);
5195 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
5204 WNDPROC consoleTextWindowProc;
5205 WNDPROC PvWindowProc;
5206 WNDPROC NameWindowProc;
5209 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
5211 char buf[MSG_SIZ], name[MSG_SIZ];
5212 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5216 SetWindowText(hInput, command);
5218 SendMessage(hInput, WM_CHAR, '\r', 0);
5222 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5227 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5228 if (sel.cpMin == sel.cpMax) {
5229 /* Expand to surrounding word */
5232 tr.chrg.cpMax = sel.cpMin;
5233 tr.chrg.cpMin = --sel.cpMin;
5234 if (sel.cpMin < 0) break;
5235 tr.lpstrText = name;
5236 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5237 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5241 tr.chrg.cpMin = sel.cpMax;
5242 tr.chrg.cpMax = ++sel.cpMax;
5243 tr.lpstrText = name;
5244 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
5245 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5248 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5249 MessageBeep(MB_ICONEXCLAMATION);
5253 tr.lpstrText = name;
5254 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5256 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5257 MessageBeep(MB_ICONEXCLAMATION);
5260 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
5263 sprintf(buf, "%s %s", command, name);
5264 SetWindowText(hInput, buf);
5265 SendMessage(hInput, WM_CHAR, '\r', 0);
5267 sprintf(buf, "%s %s ", command, name); /* trailing space */
5268 SetWindowText(hInput, buf);
5271 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5277 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5284 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5287 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
5292 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5293 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
5298 if (wParam == '\t') {
5299 if (GetKeyState(VK_SHIFT) < 0) {
5301 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5302 if (buttonDesc[0].hwnd) {
5303 SetFocus(buttonDesc[0].hwnd);
5309 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
5312 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5314 SendMessage(hInput, message, wParam, lParam);
5318 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5320 return SendMessage(hInput, message, wParam, lParam);
5321 case WM_MBUTTONDOWN:
5322 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5323 case WM_RBUTTONDOWN:
5324 if (!(GetKeyState(VK_SHIFT) & ~1)) {
5325 /* Move selection here if it was empty */
5327 pt.x = LOWORD(lParam);
5328 pt.y = HIWORD(lParam);
5329 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5330 if (sel.cpMin == sel.cpMax) {
5331 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
5332 sel.cpMax = sel.cpMin;
5333 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5335 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
5339 if (GetKeyState(VK_SHIFT) & ~1) {
5340 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5341 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5344 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
5345 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5346 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5347 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5349 pt.x = LOWORD(lParam);
5350 pt.y = HIWORD(lParam);
5351 MenuPopup(hwnd, pt, hmenu, -1);
5355 switch (LOWORD(wParam)) {
5356 case IDM_QuickPaste:
5358 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5359 if (sel.cpMin == sel.cpMax) {
5360 MessageBeep(MB_ICONEXCLAMATION);
5363 SendMessage(hwnd, WM_COPY, 0, 0);
5364 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5365 SendMessage(hInput, WM_PASTE, 0, 0);
5370 SendMessage(hwnd, WM_CUT, 0, 0);
5373 SendMessage(hwnd, WM_PASTE, 0, 0);
5376 SendMessage(hwnd, WM_COPY, 0, 0);
5380 int i = LOWORD(wParam) - IDM_CommandX;
5381 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
5382 icsTextMenuEntry[i].command != NULL) {
5383 CommandX(hwnd, icsTextMenuEntry[i].command,
5384 icsTextMenuEntry[i].getname,
5385 icsTextMenuEntry[i].immediate);
5393 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
5396 WNDPROC consoleInputWindowProc;
5399 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5403 static BOOL sendNextChar = FALSE;
5404 static BOOL quoteNextChar = FALSE;
5405 InputSource *is = consoleInputSource;
5411 if (!appData.localLineEditing || sendNextChar) {
5412 is->buf[0] = (CHAR) wParam;
5414 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5415 sendNextChar = FALSE;
5418 if (quoteNextChar) {
5419 buf[0] = (char) wParam;
5421 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
5422 quoteNextChar = FALSE;
5426 case '\r': /* Enter key */
5427 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
5428 if (consoleEcho) SaveInHistory(is->buf);
5429 is->buf[is->count++] = '\n';
5430 is->buf[is->count] = NULLCHAR;
5431 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5433 ConsoleOutput(is->buf, is->count, TRUE);
5434 } else if (appData.localLineEditing) {
5435 ConsoleOutput("\n", 1, TRUE);
5438 case '\033': /* Escape key */
5439 SetWindowText(hwnd, "");
5440 cf.cbSize = sizeof(CHARFORMAT);
5441 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
5443 cf.crTextColor = textAttribs[ColorNormal].color;
5445 cf.crTextColor = COLOR_ECHOOFF;
5447 cf.dwEffects = textAttribs[ColorNormal].effects;
5448 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
5450 case '\t': /* Tab key */
5451 if (GetKeyState(VK_SHIFT) < 0) {
5453 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
5456 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5457 if (buttonDesc[0].hwnd) {
5458 SetFocus(buttonDesc[0].hwnd);
5464 case '\023': /* Ctrl+S */
5465 sendNextChar = TRUE;
5467 case '\021': /* Ctrl+Q */
5468 quoteNextChar = TRUE;
5477 GetWindowText(hwnd, buf, MSG_SIZ);
5478 p = PrevInHistory(buf);
5480 SetWindowText(hwnd, p);
5483 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5488 p = NextInHistory();
5490 SetWindowText(hwnd, p);
5493 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5499 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5503 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
5507 case WM_MBUTTONDOWN:
5508 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5509 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5512 if (GetKeyState(VK_SHIFT) & ~1) {
5513 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5514 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5518 hmenu = LoadMenu(hInst, "InputMenu");
5519 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5520 if (sel.cpMin == sel.cpMax) {
5521 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5522 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
5524 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5525 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5527 pt.x = LOWORD(lParam);
5528 pt.y = HIWORD(lParam);
5529 MenuPopup(hwnd, pt, hmenu, -1);
5533 switch (LOWORD(wParam)) {
5535 SendMessage(hwnd, EM_UNDO, 0, 0);
5539 sel.cpMax = -1; /*999999?*/
5540 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5543 SendMessage(hwnd, WM_CUT, 0, 0);
5546 SendMessage(hwnd, WM_PASTE, 0, 0);
5549 SendMessage(hwnd, WM_COPY, 0, 0);
5554 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
5557 #define CO_MAX 100000
5558 #define CO_TRIM 1000
5561 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5563 static HWND hText, hInput, hFocus;
5564 InputSource *is = consoleInputSource;
5566 static int sizeX, sizeY;
5567 int newSizeX, newSizeY;
5571 case WM_INITDIALOG: /* message: initialize dialog box */
5573 hText = GetDlgItem(hDlg, OPT_ConsoleText);
5574 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
5576 consoleTextWindowProc = (WNDPROC)
5577 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
5578 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5579 consoleInputWindowProc = (WNDPROC)
5580 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
5581 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5582 Colorize(ColorNormal, TRUE);
5583 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
5584 ChangedConsoleFont();
5585 GetClientRect(hDlg, &rect);
5587 sizeY = rect.bottom;
5588 if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&
5589 consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {
5591 EnsureOnScreen(&consoleX, &consoleY);
5592 wp.length = sizeof(WINDOWPLACEMENT);
5594 wp.showCmd = SW_SHOW;
5595 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
5596 wp.rcNormalPosition.left = consoleX;
5597 wp.rcNormalPosition.right = consoleX + consoleW;
5598 wp.rcNormalPosition.top = consoleY;
5599 wp.rcNormalPosition.bottom = consoleY + consoleH;
5600 SetWindowPlacement(hDlg, &wp);
5614 if (IsIconic(hDlg)) break;
5615 newSizeX = LOWORD(lParam);
5616 newSizeY = HIWORD(lParam);
5617 if (sizeX != newSizeX || sizeY != newSizeY) {
5618 RECT rectText, rectInput;
5620 int newTextHeight, newTextWidth;
5621 GetWindowRect(hText, &rectText);
5622 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
5623 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
5624 if (newTextHeight < 0) {
5625 newSizeY += -newTextHeight;
5628 SetWindowPos(hText, NULL, 0, 0,
5629 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
5630 GetWindowRect(hInput, &rectInput); /* gives screen coords */
5631 pt.x = rectInput.left;
5632 pt.y = rectInput.top + newSizeY - sizeY;
5633 ScreenToClient(hDlg, &pt);
5634 SetWindowPos(hInput, NULL,
5635 pt.x, pt.y, /* needs client coords */
5636 rectInput.right - rectInput.left + newSizeX - sizeX,
5637 rectInput.bottom - rectInput.top, SWP_NOZORDER);
5643 case WM_GETMINMAXINFO:
5644 /* Prevent resizing window too small */
5645 mmi = (MINMAXINFO *) lParam;
5646 mmi->ptMinTrackSize.x = 100;
5647 mmi->ptMinTrackSize.y = 100;
5650 return DefWindowProc(hDlg, message, wParam, lParam);
5658 if (hwndConsole) return;
5659 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
5660 SendMessage(hCons, WM_INITDIALOG, 0, 0);
5663 BOOL httpseglight_continued = FALSE;
5664 char *spitlight(HWND hText, char *p, ColorClass cc, CHARRANGE *sel) {
5668 httpseglight_continued = TRUE;
5672 case '\n': case '\r': case ' ': case '\t':
5673 backup = *p; *p = 0; httpseglight_continued = FALSE; break;
5677 effects = consoleCF.dwEffects;
5678 consoleCF.crTextColor = textAttribs[ColorKibitz].color;
5679 consoleCF.dwEffects |= CFM_UNDERLINE;
5680 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)sel);
5681 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
5682 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) urlstart);
5683 sel->cpMax = sel->cpMin += p - urlstart;
5684 consoleCF.crTextColor = textAttribs[currentColorClass].color;
5685 consoleCF.dwEffects = effects;
5686 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)sel);
5687 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
5692 char *strnpstr(char *p, char *needle, char *end) {
5697 if (o==needle) loc = p;
5700 if (*o == 0) return loc;
5705 int seglight(HWND hText, char *p, char *end, char *seg, int seglen, ColorClass cc, CHARRANGE *sel) {
5706 char *op, *urlstart;
5707 if (p==NULL || seg == NULL || sel == NULL) return -1;
5708 if (httpseglight_continued) {
5709 p = spitlight(hText, p, cc, sel);
5711 while (end-p >= seglen && (urlstart = strnpstr(p, seg, end)) != NULL) {
5714 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) p);
5715 sel->cpMax = sel->cpMin += op - p;
5717 p = spitlight(hText, op, cc, sel);
5720 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) p);
5721 sel->cpMax = sel->cpMin += end-p;
5727 ConsoleOutput(char* data, int length, int forceVisible)
5735 static int delayLF = 0;
5736 CHARRANGE savesel, sel;
5738 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
5754 } else if (*p == '\007') {
5755 MyPlaySound(&sounds[(int)SoundBell]);
5762 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
5763 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5764 /* Save current selection */
5765 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
5766 exlen = GetWindowTextLength(hText);
5767 /* Find out whether current end of text is visible */
5768 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
5769 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
5770 /* Trim existing text if it's too long */
5771 if (exlen + (q - buf) > CO_MAX) {
5772 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
5775 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5776 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
5778 savesel.cpMin -= trim;
5779 savesel.cpMax -= trim;
5780 if (exlen < 0) exlen = 0;
5781 if (savesel.cpMin < 0) savesel.cpMin = 0;
5782 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
5784 /* Append the new text */
5787 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5788 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
5790 /* append text while checking for and highlight links */
5791 seglight(hText, buf, q, "http://", 7, ColorKibitz, &sel);
5793 if (forceVisible || exlen == 0 ||
5794 (rect.left <= pEnd.x && pEnd.x < rect.right &&
5795 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
5796 /* Scroll to make new end of text visible if old end of text
5797 was visible or new text is an echo of user typein */
5798 sel.cpMin = 9999999;
5799 sel.cpMax = 9999999;
5800 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5801 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5802 SendMessage(hText, EM_SCROLLCARET, 0, 0);
5803 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5805 if (savesel.cpMax == exlen || forceVisible) {
5806 /* Move insert point to new end of text if it was at the old
5807 end of text or if the new text is an echo of user typein */
5808 sel.cpMin = 9999999;
5809 sel.cpMax = 9999999;
5810 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5812 /* Restore previous selection */
5813 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
5815 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5822 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
5823 RECT *rect, char *color)
5827 COLORREF oldFg, oldBg;
5830 if (appData.clockMode) {
5832 sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));
5834 sprintf(buf, "%s: %s", color, TimeString(timeRemaining));
5841 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
5842 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
5844 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
5845 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
5847 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
5849 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
5850 rect->top, ETO_CLIPPED|ETO_OPAQUE,
5851 rect, str, strlen(str), NULL);
5853 (void) SetTextColor(hdc, oldFg);
5854 (void) SetBkColor(hdc, oldBg);
5855 (void) SelectObject(hdc, oldFont);
5860 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5865 ResetEvent(ovl->hEvent);
5866 ovl->Offset = ovl->OffsetHigh = 0;
5867 ok = ReadFile(hFile, buf, count, outCount, ovl);
5871 err = GetLastError();
5872 if (err == ERROR_IO_PENDING) {
5873 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5877 err = GetLastError();
5884 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5889 ResetEvent(ovl->hEvent);
5890 ovl->Offset = ovl->OffsetHigh = 0;
5891 ok = WriteFile(hFile, buf, count, outCount, ovl);
5895 err = GetLastError();
5896 if (err == ERROR_IO_PENDING) {
5897 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5901 err = GetLastError();
5909 InputThread(LPVOID arg)
5914 is = (InputSource *) arg;
5915 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
5916 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
5917 while (is->hThread != NULL) {
5918 is->error = DoReadFile(is->hFile, is->next,
5919 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5921 if (is->error == NO_ERROR) {
5922 is->next += is->count;
5924 if (is->error == ERROR_BROKEN_PIPE) {
5925 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5928 is->count = (DWORD) -1;
5931 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5932 if (is->count <= 0) break; /* Quit on EOF or error */
5934 CloseHandle(ovl.hEvent);
5935 CloseHandle(is->hFile);
5940 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
5942 NonOvlInputThread(LPVOID arg)
5949 is = (InputSource *) arg;
5950 while (is->hThread != NULL) {
5951 is->error = ReadFile(is->hFile, is->next,
5952 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5953 &is->count, NULL) ? NO_ERROR : GetLastError();
5954 if (is->error == NO_ERROR) {
5955 /* Change CRLF to LF */
5956 if (is->next > is->buf) {
5966 if (prev == '\r' && *p == '\n') {
5978 if (is->error == ERROR_BROKEN_PIPE) {
5979 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5982 is->count = (DWORD) -1;
5985 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5986 if (is->count < 0) break; /* Quit on error */
5988 CloseHandle(is->hFile);
5993 SocketInputThread(LPVOID arg)
5997 is = (InputSource *) arg;
5998 while (is->hThread != NULL) {
5999 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
6000 if ((int)is->count == SOCKET_ERROR) {
6001 is->count = (DWORD) -1;
6002 is->error = WSAGetLastError();
6004 is->error = NO_ERROR;
6005 is->next += is->count;
6006 if (is->count == 0 && is->second == is) {
6007 /* End of file on stderr; quit with no message */
6011 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
6012 if (is->count <= 0) break; /* Quit on EOF or error */
6018 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
6022 is = (InputSource *) lParam;
6023 if (is->lineByLine) {
6024 /* Feed in lines one by one */
6027 while (q < is->next) {
6029 (is->func)(is, is->closure, p, q - p, NO_ERROR);
6033 /* Move any partial line to the start of the buffer */
6035 while (p < is->next) {
6039 if (is->error != NO_ERROR || is->count == 0) {
6040 /* Notify backend of the error. Note: If there was a partial
6041 line at the end, it is not flushed through. */
6042 (is->func)(is, is->closure, is->buf, is->count, is->error);
6045 /* Feed in the whole chunk of input at once */
6046 (is->func)(is, is->closure, is->buf, is->count, is->error);
6051 /*---------------------------------------------------------------------------*\
6053 * Menu enables. Used when setting various modes.
6055 \*---------------------------------------------------------------------------*/
6063 SetMenuEnables(HMENU hmenu, Enables *enab)
6065 while (enab->item > 0) {
6066 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
6071 Enables gnuEnables[] = {
6072 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6073 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6074 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6075 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
6076 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
6077 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
6078 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6079 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
6080 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
6081 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6085 Enables icsEnables[] = {
6086 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6087 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6088 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6089 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6090 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6091 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6092 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6093 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6094 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6095 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6096 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6097 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
6102 Enables zippyEnables[] = {
6103 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6104 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
6105 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
6110 Enables ncpEnables[] = {
6111 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6112 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6113 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6114 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6115 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6116 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6117 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6118 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6119 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
6120 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6121 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6122 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6123 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6124 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6125 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6129 Enables trainingOnEnables[] = {
6130 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
6131 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
6132 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
6133 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
6134 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
6135 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
6136 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6137 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
6141 Enables trainingOffEnables[] = {
6142 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
6143 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
6144 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
6145 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
6146 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
6147 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
6148 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6149 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
6153 /* These modify either ncpEnables or gnuEnables */
6154 Enables cmailEnables[] = {
6155 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
6156 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
6157 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
6158 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
6159 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
6160 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6161 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
6165 Enables machineThinkingEnables[] = {
6166 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
6167 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
6168 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
6169 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
6170 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
6171 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
6172 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
6173 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
6174 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
6175 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
6176 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6177 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6178 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6179 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
6180 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6184 Enables userThinkingEnables[] = {
6185 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
6186 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
6187 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
6188 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
6189 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
6190 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
6191 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
6192 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
6193 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
6194 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
6195 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
6196 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
6197 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
6198 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
6199 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
6203 /*---------------------------------------------------------------------------*\
6205 * Front-end interface functions exported by XBoard.
6206 * Functions appear in same order as prototypes in frontend.h.
6208 \*---------------------------------------------------------------------------*/
6212 static UINT prevChecked = 0;
6213 static int prevPausing = 0;
6216 if (pausing != prevPausing) {
6217 prevPausing = pausing;
6218 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
6219 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
6220 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
6224 case BeginningOfGame:
6225 if (appData.icsActive)
6226 nowChecked = IDM_IcsClient;
6227 else if (appData.noChessProgram)
6228 nowChecked = IDM_EditGame;
6230 nowChecked = IDM_MachineBlack;
6232 case MachinePlaysBlack:
6233 nowChecked = IDM_MachineBlack;
6235 case MachinePlaysWhite:
6236 nowChecked = IDM_MachineWhite;
6238 case TwoMachinesPlay:
6239 nowChecked = IDM_TwoMachines;
6242 nowChecked = IDM_AnalysisMode;
6245 nowChecked = IDM_AnalyzeFile;
6248 nowChecked = IDM_EditGame;
6250 case PlayFromGameFile:
6251 nowChecked = IDM_LoadGame;
6254 nowChecked = IDM_EditPosition;
6257 nowChecked = IDM_Training;
6259 case IcsPlayingWhite:
6260 case IcsPlayingBlack:
6263 nowChecked = IDM_IcsClient;
6270 if (prevChecked != 0)
6271 (void) CheckMenuItem(GetMenu(hwndMain),
6272 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
6273 if (nowChecked != 0)
6274 (void) CheckMenuItem(GetMenu(hwndMain),
6275 nowChecked, MF_BYCOMMAND|MF_CHECKED);
6277 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
6278 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
6279 MF_BYCOMMAND|MF_ENABLED);
6281 (void) EnableMenuItem(GetMenu(hwndMain),
6282 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
6285 prevChecked = nowChecked;
6291 HMENU hmenu = GetMenu(hwndMain);
6292 SetMenuEnables(hmenu, icsEnables);
6293 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
6294 MF_BYPOSITION|MF_ENABLED);
6296 if (appData.zippyPlay) {
6297 SetMenuEnables(hmenu, zippyEnables);
6305 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
6311 HMENU hmenu = GetMenu(hwndMain);
6312 SetMenuEnables(hmenu, ncpEnables);
6313 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
6314 MF_BYPOSITION|MF_GRAYED);
6315 DrawMenuBar(hwndMain);
6321 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
6328 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
6329 for (i = 0; i < N_BUTTONS; i++) {
6330 if (buttonDesc[i].hwnd != NULL)
6331 EnableWindow(buttonDesc[i].hwnd, FALSE);
6336 VOID SetTrainingModeOff()
6339 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
6340 for (i = 0; i < N_BUTTONS; i++) {
6341 if (buttonDesc[i].hwnd != NULL)
6342 EnableWindow(buttonDesc[i].hwnd, TRUE);
6348 SetUserThinkingEnables()
6350 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
6354 SetMachineThinkingEnables()
6356 HMENU hMenu = GetMenu(hwndMain);
6357 int flags = MF_BYCOMMAND|MF_ENABLED;
6359 SetMenuEnables(hMenu, machineThinkingEnables);
6361 if (gameMode == MachinePlaysBlack) {
6362 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
6363 } else if (gameMode == MachinePlaysWhite) {
6364 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
6365 } else if (gameMode == TwoMachinesPlay) {
6366 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
6372 DisplayTitle(char *str)
6374 char title[MSG_SIZ], *host;
6375 if (str[0] != NULLCHAR) {
6377 } else if (appData.icsActive) {
6378 if (appData.icsCommPort[0] != NULLCHAR)
6381 host = appData.icsHost;
6382 sprintf(title, "%s: %s", szTitle, host);
6383 } else if (appData.noChessProgram) {
6384 strcpy(title, szTitle);
6386 strcpy(title, szTitle);
6387 strcat(title, ": ");
6388 strcat(title, first.tidy);
6390 SetWindowText(hwndMain, title);
6395 DisplayMessage(char *str1, char *str2)
6399 int remain = MESSAGE_TEXT_MAX - 1;
6402 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
6403 messageText[0] = NULLCHAR;
6406 if (len > remain) len = remain;
6407 strncpy(messageText, str1, len);
6408 messageText[len] = NULLCHAR;
6411 if (*str2 && remain >= 2) {
6413 strcat(messageText, " ");
6417 if (len > remain) len = remain;
6418 strncat(messageText, str2, len);
6420 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
6422 if (IsIconic(hwndMain)) return;
6423 hdc = GetDC(hwndMain);
6424 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
6425 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
6426 &messageRect, messageText, strlen(messageText), NULL);
6427 (void) SelectObject(hdc, oldFont);
6428 (void) ReleaseDC(hwndMain, hdc);
6432 DisplayError(char *str, int error)
6435 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
6442 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6443 NULL, error, LANG_NEUTRAL,
6444 (LPSTR) buf2, MSG_SIZ, NULL);
6446 sprintf(buf, "%s:\n%s", str, buf2);
6448 ErrorMap *em = errmap;
6449 while (em->err != 0 && em->err != error) em++;
6451 sprintf(buf, "%s:\n%s", str, em->msg);
6453 sprintf(buf, "%s:\nError code %d", str, error);
6461 if (hwndMain != NULL /*!!?*/) {
6474 if (hwndMain == NULL) {
6475 MessageBox(NULL, errorMessage, "Error", MB_OK|MB_ICONEXCLAMATION);
6477 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
6478 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
6479 hwndMain, (DLGPROC)lpProc);
6480 FreeProcInstance(lpProc);
6486 DisplayMoveError(char *str)
6490 DrawPosition(FALSE, NULL);
6491 if (appData.popupMoveErrors) {
6492 DisplayError(str, 0);
6494 DisplayMessage(str, "");
6495 moveErrorMessageUp = TRUE;
6500 DisplayFatalError(char *str, int error, int exitStatus)
6502 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
6504 char *label = exitStatus ? "Fatal Error" : "Exiting";
6507 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6508 NULL, error, LANG_NEUTRAL,
6509 (LPSTR) buf2, MSG_SIZ, NULL);
6511 sprintf(buf, "%s:\n%s", str, buf2);
6513 ErrorMap *em = errmap;
6514 while (em->err != 0 && em->err != error) em++;
6516 sprintf(buf, "%s:\n%s", str, em->msg);
6518 sprintf(buf, "%s:\nError code %d", str, error);
6523 if (appData.debugMode) {
6524 fprintf(debugFP, "%s: %s\n", label, str);
6526 if (appData.popupExitMessage) {
6527 (void) MessageBox(hwndMain, str, label, MB_OK|
6528 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
6530 ExitEvent(exitStatus);
6535 DisplayInformation(char *str)
6537 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
6542 char *title, *question, *replyPrefix;
6547 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
6549 static QuestionParams *qp;
6550 char reply[MSG_SIZ];
6555 qp = (QuestionParams *) lParam;
6556 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
6557 SetWindowText(hDlg, qp->title);
6558 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
6559 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
6563 switch (LOWORD(wParam)) {
6565 strcpy(reply, qp->replyPrefix);
6566 if (*reply) strcat(reply, " ");
6567 len = strlen(reply);
6568 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
6569 strcat(reply, "\n");
6570 OutputToProcess(qp->pr, reply, strlen(reply), &err);
6571 EndDialog(hDlg, TRUE);
6572 if (err) DisplayFatalError("Error writing to chess program", err, 1);
6575 EndDialog(hDlg, FALSE);
6586 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
6592 qp.question = question;
6593 qp.replyPrefix = replyPrefix;
6595 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
6596 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
6597 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
6598 FreeProcInstance(lpProc);
6603 DisplayIcsInteractionTitle(char *str)
6605 char consoleTitle[MSG_SIZ];
6607 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
6608 SetWindowText(hwndConsole, consoleTitle);
6612 DrawPosition(int fullRedraw, Board board)
6614 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
6622 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
6623 dragInfo.pos.x = dragInfo.pos.y = -1;
6624 dragInfo.pos.x = dragInfo.pos.y = -1;
6625 dragInfo.lastpos = dragInfo.pos;
6626 dragInfo.start.x = dragInfo.start.y = -1;
6627 dragInfo.from = dragInfo.start;
6629 DrawPosition(TRUE, NULL);
6635 CommentPopUp(char *title, char *str)
6637 HWND hwnd = GetActiveWindow();
6638 EitherCommentPopUp(0, title, str, FALSE);
6639 SetActiveWindow(hwnd);
6643 CommentPopDown(void)
6645 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
6646 if (commentDialog) {
6647 ShowWindow(commentDialog, SW_HIDE);
6649 commentDialogUp = FALSE;
6653 EditCommentPopUp(int index, char *title, char *str)
6655 EitherCommentPopUp(index, title, str, TRUE);
6662 MyPlaySound(&sounds[(int)SoundMove]);
6665 VOID PlayIcsWinSound()
6667 MyPlaySound(&sounds[(int)SoundIcsWin]);
6670 VOID PlayIcsLossSound()
6672 MyPlaySound(&sounds[(int)SoundIcsLoss]);
6675 VOID PlayIcsDrawSound()
6677 MyPlaySound(&sounds[(int)SoundIcsDraw]);
6680 VOID PlayIcsUnfinishedSound()
6682 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
6688 MyPlaySound(&sounds[(int)SoundAlarm]);
6697 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6698 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
6699 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
6708 consoleEcho = FALSE;
6709 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6710 /* This works OK: set text and background both to the same color */
6712 cf.crTextColor = COLOR_ECHOOFF;
6713 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
6714 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
6719 void Colorize(ColorClass cc, int continuation)
6721 currentColorClass = cc;
6722 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
6723 consoleCF.crTextColor = textAttribs[cc].color;
6724 consoleCF.dwEffects = textAttribs[cc].effects|CFE_LINK;
6725 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
6731 static char buf[MSG_SIZ];
6732 DWORD bufsiz = MSG_SIZ;
6734 if (!GetUserName(buf, &bufsiz)) {
6735 /*DisplayError("Error getting user name", GetLastError());*/
6736 strcpy(buf, "User");
6744 static char buf[MSG_SIZ];
6745 DWORD bufsiz = MSG_SIZ;
6747 if (!GetComputerName(buf, &bufsiz)) {
6748 /*DisplayError("Error getting host name", GetLastError());*/
6749 strcpy(buf, "Unknown");
6758 return clockTimerEvent != 0;
6764 if (clockTimerEvent == 0) return FALSE;
6765 KillTimer(hwndMain, clockTimerEvent);
6766 clockTimerEvent = 0;
6771 StartClockTimer(long millisec)
6773 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
6774 (UINT) millisec, NULL);
6778 DisplayWhiteClock(long timeRemaining, int highlight)
6781 hdc = GetDC(hwndMain);
6782 if (!IsIconic(hwndMain)) {
6783 DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");
6785 if (highlight && iconCurrent == iconBlack) {
6786 iconCurrent = iconWhite;
6787 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6788 if (IsIconic(hwndMain)) {
6789 DrawIcon(hdc, 2, 2, iconCurrent);
6792 (void) ReleaseDC(hwndMain, hdc);
6794 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6798 DisplayBlackClock(long timeRemaining, int highlight)
6801 hdc = GetDC(hwndMain);
6802 if (!IsIconic(hwndMain)) {
6803 DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");
6805 if (highlight && iconCurrent == iconWhite) {
6806 iconCurrent = iconBlack;
6807 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6808 if (IsIconic(hwndMain)) {
6809 DrawIcon(hdc, 2, 2, iconCurrent);
6812 (void) ReleaseDC(hwndMain, hdc);
6814 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6819 LoadGameTimerRunning()
6821 return loadGameTimerEvent != 0;
6827 if (loadGameTimerEvent == 0) return FALSE;
6828 KillTimer(hwndMain, loadGameTimerEvent);
6829 loadGameTimerEvent = 0;
6834 StartLoadGameTimer(long millisec)
6836 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
6837 (UINT) millisec, NULL);
6845 char fileTitle[MSG_SIZ];
6847 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
6848 f = OpenFileDialog(hwndMain, TRUE, defName,
6849 appData.oldSaveStyle ? "gam" : "pgn",
6851 "Save Game to File", NULL, fileTitle, NULL);
6860 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
6862 if (delayedTimerEvent != 0) {
6863 if (appData.debugMode) {
6864 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
6866 KillTimer(hwndMain, delayedTimerEvent);
6867 delayedTimerEvent = 0;
6868 delayedTimerCallback();
6870 delayedTimerCallback = cb;
6871 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
6872 (UINT) millisec, NULL);
6875 DelayedEventCallback
6878 if (delayedTimerEvent) {
6879 return delayedTimerCallback;
6886 CancelDelayedEvent()
6888 if (delayedTimerEvent) {
6889 KillTimer(hwndMain, delayedTimerEvent);
6890 delayedTimerEvent = 0;
6894 /* Start a child process running the given program.
6895 The process's standard output can be read from "from", and its
6896 standard input can be written to "to".
6897 Exit with fatal error if anything goes wrong.
6898 Returns an opaque pointer that can be used to destroy the process
6902 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
6904 #define BUFSIZE 4096
6906 HANDLE hChildStdinRd, hChildStdinWr,
6907 hChildStdoutRd, hChildStdoutWr;
6908 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
6909 SECURITY_ATTRIBUTES saAttr;
6911 PROCESS_INFORMATION piProcInfo;
6912 STARTUPINFO siStartInfo;
6917 if (appData.debugMode) {
6918 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
6923 /* Set the bInheritHandle flag so pipe handles are inherited. */
6924 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
6925 saAttr.bInheritHandle = TRUE;
6926 saAttr.lpSecurityDescriptor = NULL;
6929 * The steps for redirecting child's STDOUT:
6930 * 1. Create anonymous pipe to be STDOUT for child.
6931 * 2. Create a noninheritable duplicate of read handle,
6932 * and close the inheritable read handle.
6935 /* Create a pipe for the child's STDOUT. */
6936 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
6937 return GetLastError();
6940 /* Duplicate the read handle to the pipe, so it is not inherited. */
6941 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
6942 GetCurrentProcess(), &hChildStdoutRdDup, 0,
6943 FALSE, /* not inherited */
6944 DUPLICATE_SAME_ACCESS);
6946 return GetLastError();
6948 CloseHandle(hChildStdoutRd);
6951 * The steps for redirecting child's STDIN:
6952 * 1. Create anonymous pipe to be STDIN for child.
6953 * 2. Create a noninheritable duplicate of write handle,
6954 * and close the inheritable write handle.
6957 /* Create a pipe for the child's STDIN. */
6958 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
6959 return GetLastError();
6962 /* Duplicate the write handle to the pipe, so it is not inherited. */
6963 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
6964 GetCurrentProcess(), &hChildStdinWrDup, 0,
6965 FALSE, /* not inherited */
6966 DUPLICATE_SAME_ACCESS);
6968 return GetLastError();
6970 CloseHandle(hChildStdinWr);
6972 /* Arrange to (1) look in dir for the child .exe file, and
6973 * (2) have dir be the child's working directory. Interpret
6974 * dir relative to the directory WinBoard loaded from. */
6975 GetCurrentDirectory(MSG_SIZ, buf);
6976 SetCurrentDirectory(installDir);
6977 SetCurrentDirectory(dir);
6979 /* Now create the child process. */
6981 siStartInfo.cb = sizeof(STARTUPINFO);
6982 siStartInfo.lpReserved = NULL;
6983 siStartInfo.lpDesktop = NULL;
6984 siStartInfo.lpTitle = NULL;
6985 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
6986 siStartInfo.cbReserved2 = 0;
6987 siStartInfo.lpReserved2 = NULL;
6988 siStartInfo.hStdInput = hChildStdinRd;
6989 siStartInfo.hStdOutput = hChildStdoutWr;
6990 siStartInfo.hStdError = hChildStdoutWr;
6992 fSuccess = CreateProcess(NULL,
6993 cmdLine, /* command line */
6994 NULL, /* process security attributes */
6995 NULL, /* primary thread security attrs */
6996 TRUE, /* handles are inherited */
6997 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
6998 NULL, /* use parent's environment */
7000 &siStartInfo, /* STARTUPINFO pointer */
7001 &piProcInfo); /* receives PROCESS_INFORMATION */
7003 err = GetLastError();
7004 SetCurrentDirectory(buf); /* return to prev directory */
7009 /* Close the handles we don't need in the parent */
7010 CloseHandle(piProcInfo.hThread);
7011 CloseHandle(hChildStdinRd);
7012 CloseHandle(hChildStdoutWr);
7014 /* Prepare return value */
7015 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7017 cp->hProcess = piProcInfo.hProcess;
7018 cp->pid = piProcInfo.dwProcessId;
7019 cp->hFrom = hChildStdoutRdDup;
7020 cp->hTo = hChildStdinWrDup;
7024 /* Klaus Friedel says that this Sleep solves a problem under Windows
7025 2000 where engines sometimes don't see the initial command(s)
7026 from WinBoard and hang. I don't understand how that can happen,
7027 but the Sleep is harmless, so I've put it in. Others have also
7028 reported what may be the same problem, so hopefully this will fix
7037 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
7041 cp = (ChildProc *) pr;
7042 if (cp == NULL) return;
7046 /* TerminateProcess is considered harmful, so... */
7047 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
7048 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
7049 /* The following doesn't work because the chess program
7050 doesn't "have the same console" as WinBoard. Maybe
7051 we could arrange for this even though neither WinBoard
7052 nor the chess program uses a console for stdio? */
7053 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
7054 CloseHandle(cp->hProcess);
7058 if (cp->hFrom) CloseHandle(cp->hFrom);
7062 closesocket(cp->sock);
7067 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
7068 closesocket(cp->sock);
7069 closesocket(cp->sock2);
7077 InterruptChildProcess(ProcRef pr)
7081 cp = (ChildProc *) pr;
7082 if (cp == NULL) return;
7085 /* The following doesn't work because the chess program
7086 doesn't "have the same console" as WinBoard. Maybe
7087 we could arrange for this even though neither WinBoard
7088 nor the chess program uses a console for stdio */
7089 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
7094 /* Can't interrupt */
7098 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
7105 OpenTelnet(char *host, char *port, ProcRef *pr)
7107 char cmdLine[MSG_SIZ];
7109 if (port[0] == NULLCHAR) {
7110 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
7112 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
7114 return StartChildProcess(cmdLine, "", pr);
7118 /* Code to open TCP sockets */
7121 OpenTCP(char *host, char *port, ProcRef *pr)
7126 struct sockaddr_in sa, mysa;
7127 struct hostent FAR *hp;
7128 unsigned short uport;
7129 WORD wVersionRequested;
7132 /* Initialize socket DLL */
7133 wVersionRequested = MAKEWORD(1, 1);
7134 err = WSAStartup(wVersionRequested, &wsaData);
7135 if (err != 0) return err;
7138 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7139 err = WSAGetLastError();
7144 /* Bind local address using (mostly) don't-care values.
7146 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7147 mysa.sin_family = AF_INET;
7148 mysa.sin_addr.s_addr = INADDR_ANY;
7149 uport = (unsigned short) 0;
7150 mysa.sin_port = htons(uport);
7151 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7153 err = WSAGetLastError();
7158 /* Resolve remote host name */
7159 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7160 if (!(hp = gethostbyname(host))) {
7161 unsigned int b0, b1, b2, b3;
7163 err = WSAGetLastError();
7165 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7166 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7167 hp->h_addrtype = AF_INET;
7169 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7170 hp->h_addr_list[0] = (char *) malloc(4);
7171 hp->h_addr_list[0][0] = (char) b0;
7172 hp->h_addr_list[0][1] = (char) b1;
7173 hp->h_addr_list[0][2] = (char) b2;
7174 hp->h_addr_list[0][3] = (char) b3;
7180 sa.sin_family = hp->h_addrtype;
7181 uport = (unsigned short) atoi(port);
7182 sa.sin_port = htons(uport);
7183 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7185 /* Make connection */
7186 if (connect(s, (struct sockaddr *) &sa,
7187 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7188 err = WSAGetLastError();
7193 /* Prepare return value */
7194 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7197 *pr = (ProcRef *) cp;
7203 OpenCommPort(char *name, ProcRef *pr)
7208 char fullname[MSG_SIZ];
7211 sprintf(fullname, "\\\\.\\%s", name);
7213 strcpy(fullname, name);
7215 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
7216 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
7217 if (h == (HANDLE) -1) {
7218 return GetLastError();
7222 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
7224 /* Accumulate characters until a 100ms pause, then parse */
7225 ct.ReadIntervalTimeout = 100;
7226 ct.ReadTotalTimeoutMultiplier = 0;
7227 ct.ReadTotalTimeoutConstant = 0;
7228 ct.WriteTotalTimeoutMultiplier = 0;
7229 ct.WriteTotalTimeoutConstant = 0;
7230 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
7232 /* Prepare return value */
7233 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7237 *pr = (ProcRef *) cp;
7243 OpenLoopback(ProcRef *pr)
7245 DisplayFatalError("Not implemented", 0, 1);
7251 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
7256 struct sockaddr_in sa, mysa;
7257 struct hostent FAR *hp;
7258 unsigned short uport;
7259 WORD wVersionRequested;
7262 char stderrPortStr[MSG_SIZ];
7264 /* Initialize socket DLL */
7265 wVersionRequested = MAKEWORD(1, 1);
7266 err = WSAStartup(wVersionRequested, &wsaData);
7267 if (err != 0) return err;
7269 /* Resolve remote host name */
7270 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7271 if (!(hp = gethostbyname(host))) {
7272 unsigned int b0, b1, b2, b3;
7274 err = WSAGetLastError();
7276 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7277 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7278 hp->h_addrtype = AF_INET;
7280 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7281 hp->h_addr_list[0] = (char *) malloc(4);
7282 hp->h_addr_list[0][0] = (char) b0;
7283 hp->h_addr_list[0][1] = (char) b1;
7284 hp->h_addr_list[0][2] = (char) b2;
7285 hp->h_addr_list[0][3] = (char) b3;
7291 sa.sin_family = hp->h_addrtype;
7292 uport = (unsigned short) 514;
7293 sa.sin_port = htons(uport);
7294 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7296 /* Bind local socket to unused "privileged" port address
7299 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7300 mysa.sin_family = AF_INET;
7301 mysa.sin_addr.s_addr = INADDR_ANY;
7302 for (fromPort = 1023;; fromPort--) {
7305 return WSAEADDRINUSE;
7307 if (s == INVALID_SOCKET) {
7308 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7309 err = WSAGetLastError();
7314 uport = (unsigned short) fromPort;
7315 mysa.sin_port = htons(uport);
7316 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7318 err = WSAGetLastError();
7319 if (err == WSAEADDRINUSE) continue;
7323 if (connect(s, (struct sockaddr *) &sa,
7324 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7325 err = WSAGetLastError();
7326 if (err == WSAEADDRINUSE) {
7337 /* Bind stderr local socket to unused "privileged" port address
7339 s2 = INVALID_SOCKET;
7340 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7341 mysa.sin_family = AF_INET;
7342 mysa.sin_addr.s_addr = INADDR_ANY;
7343 for (fromPort = 1023;; fromPort--) {
7345 (void) closesocket(s);
7347 return WSAEADDRINUSE;
7349 if (s2 == INVALID_SOCKET) {
7350 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7351 err = WSAGetLastError();
7357 uport = (unsigned short) fromPort;
7358 mysa.sin_port = htons(uport);
7359 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7361 err = WSAGetLastError();
7362 if (err == WSAEADDRINUSE) continue;
7363 (void) closesocket(s);
7367 if (listen(s2, 1) == SOCKET_ERROR) {
7368 err = WSAGetLastError();
7369 if (err == WSAEADDRINUSE) {
7371 s2 = INVALID_SOCKET;
7374 (void) closesocket(s);
7375 (void) closesocket(s2);
7381 sprintf(stderrPortStr, "%d", fromPort);
7383 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
7384 err = WSAGetLastError();
7385 (void) closesocket(s);
7386 (void) closesocket(s2);
7391 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
7392 err = WSAGetLastError();
7393 (void) closesocket(s);
7394 (void) closesocket(s2);
7398 if (*user == NULLCHAR) user = UserName();
7399 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
7400 err = WSAGetLastError();
7401 (void) closesocket(s);
7402 (void) closesocket(s2);
7406 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
7407 err = WSAGetLastError();
7408 (void) closesocket(s);
7409 (void) closesocket(s2);
7414 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
7415 err = WSAGetLastError();
7416 (void) closesocket(s);
7417 (void) closesocket(s2);
7421 (void) closesocket(s2); /* Stop listening */
7423 /* Prepare return value */
7424 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7428 *pr = (ProcRef *) cp;
7435 AddInputSource(ProcRef pr, int lineByLine,
7436 InputCallback func, VOIDSTAR closure)
7438 InputSource *is, *is2;
7439 ChildProc *cp = (ChildProc *) pr;
7441 is = (InputSource *) calloc(1, sizeof(InputSource));
7442 is->lineByLine = lineByLine;
7444 is->closure = closure;
7449 consoleInputSource = is;
7451 is->kind = cp->kind;
7454 is->hFile = cp->hFrom;
7455 cp->hFrom = NULL; /* now owned by InputThread */
7457 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
7458 (LPVOID) is, 0, &is->id);
7462 is->hFile = cp->hFrom;
7463 cp->hFrom = NULL; /* now owned by InputThread */
7465 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
7466 (LPVOID) is, 0, &is->id);
7470 is->sock = cp->sock;
7472 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7473 (LPVOID) is, 0, &is->id);
7477 is2 = (InputSource *) calloc(1, sizeof(InputSource));
7479 is->sock = cp->sock;
7481 is2->sock = cp->sock2;
7484 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7485 (LPVOID) is, 0, &is->id);
7487 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7488 (LPVOID) is2, 0, &is2->id);
7492 return (InputSourceRef) is;
7496 RemoveInputSource(InputSourceRef isr)
7500 is = (InputSource *) isr;
7501 is->hThread = NULL; /* tell thread to stop */
7502 CloseHandle(is->hThread);
7503 if (is->second != NULL) {
7504 is->second->hThread = NULL;
7505 CloseHandle(is->second->hThread);
7511 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
7514 int outCount = SOCKET_ERROR;
7515 ChildProc *cp = (ChildProc *) pr;
7516 static OVERLAPPED ovl;
7519 ConsoleOutput(message, count, FALSE);
7523 if (ovl.hEvent == NULL) {
7524 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
7526 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
7531 outCount = send(cp->sock, message, count, 0);
7532 if (outCount == SOCKET_ERROR) {
7533 *outError = WSAGetLastError();
7535 *outError = NO_ERROR;
7540 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
7541 &dOutCount, NULL)) {
7542 *outError = NO_ERROR;
7543 outCount = (int) dOutCount;
7545 *outError = GetLastError();
7550 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
7552 if (*outError == NO_ERROR) {
7553 outCount = (int) dOutCount;
7561 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
7564 /* Ignore delay, not implemented for WinBoard */
7565 return OutputToProcess(pr, message, count, outError);
7570 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
7571 char *buf, int count, int error)
7573 DisplayFatalError("Not implemented", 0, 1);
7576 /* see wgamelist.c for Game List functions */
7577 /* see wedittags.c for Edit Tags functions */
7587 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
7588 f = fopen(buf, "r");
7590 ProcessICSInitScript(f);
7598 StartAnalysisClock()
7600 if (analysisTimerEvent) return;
7601 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
7606 NameSubClass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
7621 return (*NameWindowProc)(hwnd, message, wParam, lParam);
7626 PvSubClass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
7631 pvDisplay = GetDlgItem(analysisDialog, OPT_AnalysisText);
7638 pt.x = LOWORD(lParam);
7639 pt.y = HIWORD(lParam);
7640 if (appData.icsActive) {
7641 MenuPopup(pvDisplay, pt, LoadMenu(hInst, "ICSENGINEROOM"), -1);
7643 MenuPopup(pvDisplay, pt, LoadMenu(hInst, "ENGINEROOM"), -1);
7648 switch (LOWORD(wParam)) {
7650 GetSel = SendMessage(hwnd, LB_GETCOUNT, 0, 0);
7651 SendMessage(hwnd, LB_SETSEL, GetSel, -1);
7653 GetSel = SendMessage(hwnd, LB_GETCOUNT, 0, 0);
7654 SendMessage(hwnd, LB_SETSEL, GetSel, -1);
7655 SendMessage(hwnd, LB_GETTEXT, 0, (LPARAM)&buf);
7658 SendMessage(hwnd, WM_COPY, 0, 0);
7660 GetSel = SendMessage(hwnd, LB_GETCOUNT, 0, 0);
7661 SendMessage(hwnd, LB_SETSEL, GetSel, -1);
7662 SendMessage(hwnd, LB_GETTEXT, 0, (LPARAM)&buf);
7665 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
7666 SendMessage(hwnd, WM_COPY, 0, 0);
7669 case IDM_MachineWhite:
7670 SendMessage(hwndMain, WM_COMMAND, IDM_MachineWhite, 0);
7672 case IDM_MachineBlack:
7673 SendMessage(hwndMain, WM_COMMAND, IDM_MachineBlack, 0);
7675 case IDM_AnalysisMode:
7676 SendMessage(hwndMain, WM_COMMAND, IDM_AnalysisMode, 0);
7681 case IDM_EnginesButton:
7682 if (first.analyzing == TRUE || first.maybeThinking == TRUE) {
7683 SetDlgItemText(analysisDialog, OPT_engineStart, "Start Engine");
7684 } else if (first.analyzing == FALSE || first.maybeThinking == FALSE) {
7685 SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
7689 case IDM_OperatorTime:
7694 return (*PvWindowProc)(hwnd, message, wParam, lParam);
7699 AnalysisDialogEnable(HWND hDlg)
7702 #define IS_CHECKED(x) (Boolean)IsDlgButtonChecked(hDlg, (x))
7703 appData.engineStatLine = IS_CHECKED(OPT_engineStatLine);
7704 if (!appData.engineTourneyMode) {
7705 appData.engineTourneyMode = IS_CHECKED(OPT_engineTourneyMode);
7709 #define ENABLE_DLG_ITEM(x,y) EnableWindow(GetDlgItem(hDlg,(x)), (y))
7710 if (appData.engineTourneyMode) {
7712 EnableWindow(GetDlgItem(hDlg, OPT_engineStart), FALSE);
7713 EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
7714 /* disable myself B-) */
7715 EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), FALSE);
7716 EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), FALSE);
7717 EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), FALSE);
7720 if (appData.icsActive) {
7721 EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), FALSE);
7722 if (gameMode == IcsObserving) {
7723 EnableWindow(GetDlgItem(hDlg, OPT_engineStart), FALSE);
7724 EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
7725 EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), FALSE);
7726 EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), FALSE);
7728 EnableWindow(GetDlgItem(hDlg, OPT_engineStart), FALSE);
7729 EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
7730 EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
7731 EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), TRUE);
7735 /* mh, all should allow ;-) */
7739 EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), FALSE);
7740 EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), FALSE);
7741 EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
7742 EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
7744 case MachinePlaysWhite:
7745 if (WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), TRUE);
7746 if (!WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
7747 EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
7748 EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), TRUE);
7749 EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
7751 case MachinePlaysBlack:
7752 if (!WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), TRUE);
7753 if (WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
7754 EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
7755 EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), TRUE);
7756 EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
7759 EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
7760 EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
7761 EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), TRUE);
7762 EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
7764 /* Send OPT_SendToICS only for ICS */
7765 EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), FALSE);
7766 appData.ButtonSendOutPutToICS = FALSE;
7767 appData.SendOutPutToICS = 0;
7770 #undef ENABLE_DLG_ITEM
7775 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
7777 static HWND pvDisplay;
7779 static HANDLE depth;
7781 static HANDLE nodes;
7782 static HANDLE score;
7783 static HANDLE move_nr;
7785 static HANDLE statistic;
7786 static HANDLE tourney;
7787 static HANDLE OutPutSendToICS;
7795 LPMEASUREITEMSTRUCT lpmis;
7798 static int sizeX, sizeY;
7799 int newSizeX, newSizeY, flags;
7800 static int newTextHeight, newTextWidth;
7804 COLORREF PvBkColor = RGB(224,218,222);
7805 COLORREF NameColor = RGB(171,216,173);
7806 COLORREF FailLow = RGB(255, 255, 34);
7807 COLORREF FailHigh = RGB(255, 0, 0);
7809 * COLORREF TourneyColor = RGB(235, 80, 71);
7812 case WM_INITDIALOG: /* message: initialize dialog box */
7813 /* Initialize the dialog items */
7814 pvDisplay = GetDlgItem(hDlg, OPT_AnalysisText);
7815 depth = GetDlgItem(hDlg, OPT_engineDepth);
7816 nps = GetDlgItem(hDlg, OPT_engineNPS);
7817 nodes = GetDlgItem(hDlg, OPT_engineNodes);
7818 score = GetDlgItem(hDlg, OPT_engineScore);
7819 Name = GetDlgItem(hDlg, OPT_engineName);
7820 move_nr = GetDlgItem(hDlg, OPT_engineMoveNr);
7821 time = GetDlgItem(hDlg, OPT_engineTime);
7822 statistic = GetDlgItem(hDlg, OPT_engineStatLine);
7823 tourney = GetDlgItem(hDlg, OPT_engineTourneyMode);
7824 OutPutSendToICS = GetDlgItem(hDlg, OPT_SendToICS);
7826 #define CHECK_BOX(x,y) CheckDlgButton(hDlg, (x), (BOOL)(y))
7827 #define IS_CHECKED(x) (Boolean)IsDlgButtonChecked(hDlg, (x))
7828 AnalysisDialogEnable(hDlg);
7829 SetWindowText(hDlg, "Winboard Engine Room");
7830 NameWindowProc = (WNDPROC)
7831 SetWindowLong(Name, GWL_WNDPROC, (LONG) NameSubClass);
7832 PvWindowProc = (WNDPROC)
7833 SetWindowLong(pvDisplay, GWL_WNDPROC, (LONG) PvSubClass);
7835 if (!analysisDialog) {
7836 analysisDialog = hDlg;
7837 flags = SWP_NOZORDER;
7838 GetClientRect(hDlg, &rect);
7840 sizeY = rect.bottom;
7841 SendMessage(pvDisplay, EM_SETBKGNDCOLOR, FALSE, PvBkColor);
7842 SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameColor);
7843 /* gameMode - we do a check */
7844 AnalysisDialogEnable(hDlg);
7845 if (analysisH < 200 || analysisW < 300) {
7846 /* center - safty position at startup after broken*/
7849 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
7851 if (analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {
7853 EnsureOnScreen(&analysisX, &analysisY);
7854 wp.length = sizeof(WINDOWPLACEMENT);
7855 wp.flags = WPF_RESTORETOMAXIMIZED;
7856 wp.showCmd = SW_SHOW|SW_RESTORE;
7857 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
7858 wp.rcNormalPosition.left = analysisX;
7859 wp.rcNormalPosition.right = analysisX + analysisW;
7860 wp.rcNormalPosition.top = analysisY;
7861 wp.rcNormalPosition.bottom = analysisY + analysisH;
7862 SetWindowPlacement(hDlg, &wp);
7863 GetClientRect(hDlg, &rect);
7864 GetClientRect(pvDisplay, &rectPV);
7865 GetClientRect(statistic, &rectStatistic);
7866 GetClientRect(tourney, &rectTourney);
7867 newSizeX = rect.right;
7868 newSizeY = rect.bottom;
7872 /* set focus to main window on start */
7874 /* Check Send engine output to ICS */
7875 CHECK_BOX(OPT_SendToICS, appData.ButtonSendOutPutToICS);
7879 case WM_MEASUREITEM:
7880 // lpmis = (LPMEASUREITEMSTRUCT) lparam;
7881 // GetWindowRect(GetDlgItem(pvDisplay, lpmis->CtlID), &rc);
7887 case WM_COMMAND: /* message: received a command */
7888 CHECK_BOX(OPT_engineStatLine, appData.engineStatLine);
7889 CHECK_BOX(OPT_engineTourneyMode, appData.engineTourneyMode);
7891 switch (LOWORD(wParam)) {
7892 case OPT_engineMove:
7895 case OPT_engineStart:
7896 if (first.analyzing == TRUE || first.maybeThinking == TRUE) {
7897 SetDlgItemText(analysisDialog, OPT_engineStart, "Start Engine");
7898 } else if (first.analyzing == FALSE || first.maybeThinking == FALSE) {
7899 SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
7903 case OPT_engineStatLine:
7904 /* over checkbox vars */
7907 appData.ButtonSendOutPutToICS = IS_CHECKED(OPT_SendToICS);
7909 if (appData.ButtonSendOutPutToICS) {
7910 appData.SendOutPutToICS = 1;
7912 appData.SendOutPutToICS = 0;
7915 case OPT_engineTourneyMode:
7916 /*appData.engineTourneyMode = TRUE;*/
7917 MessageBox(hDlg, "Tourney Mode: Disable a lot of WB function.\rYou can leave this mode by closing Engine Room Window !\r\
7918 This mode is for events suchs as World Champion Chips\rbut not ready at the moment.",
7919 "ICCA/FIDE Tourney Mode", MB_OK);
7920 SetWindowText(analysisDialog, "Winboard Engine Room - Tourney Mode");
7921 SetWindowText(hwndMain, "Winboard - Tourney Mode");
7924 /* TourneyMode off */
7925 if (appData.engineTourneyMode) {
7926 CheckDlgButton(hDlg, OPT_engineTourneyMode, BST_UNCHECKED);
7927 appData.engineTourneyMode = FALSE;
7928 AnalysisDialogEnable(hDlg);
7929 SetWindowText(analysisDialog, "Winboard Engine Room");
7932 if (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
7933 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
7934 appData.AnalysisWindow = FALSE;
7936 } else if (gameMode == IcsObserving) {
7937 appData.icsAnalyzeWindow = FALSE;
7941 appData.AnalysisWindow = FALSE;
7947 AnalysisDialogEnable(hDlg);
7953 newSizeX = LOWORD(lParam);
7954 newSizeY = HIWORD(lParam);
7955 if (sizeX != newSizeX || sizeY != newSizeY) {
7956 GetWindowRect(pvDisplay, &rectPV);
7957 newTextWidth = rectPV.right -
7958 rectPV.left + newSizeX - sizeX;
7959 newTextHeight = rectPV.bottom -
7960 rectPV.top + newSizeY - sizeY;
7961 if (newTextHeight < 0) {
7962 newSizeY += -newTextHeight;
7965 SetWindowPos(pvDisplay, NULL, 0, 0,
7966 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
7973 case WM_GETMINMAXINFO:
7974 mmi = (MINMAXINFO *) lParam;
7975 mmi->ptMinTrackSize.x = 518;
7976 mmi->ptMinTrackSize.y = 250;
7980 MoveWindow(pvDisplay, sizeX, sizeY, newTextWidth,
7981 newTextHeight, TRUE);
7982 /* engineRoom no longer a parent Window */
7983 /* possible that hwndMain is hidden */
7984 /* if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); */
7988 AnalysisDialogEnable(hDlg);
7996 /* style from EngineRoom */
7998 SetEngineRoomFonts()
8000 CHARFORMAT2 pvdisplayf;
8001 CHARFORMAT2 enginenamf;
8004 PvDisplay = GetDlgItem(analysisDialog, OPT_AnalysisText);
8005 Name = GetDlgItem(analysisDialog, OPT_engineName);
8008 pvdisplayf.cbSize = sizeof(CHARFORMAT);
8009 pvdisplayf.bCharSet = DEFAULT_CHARSET;
8010 pvdisplayf.dwMask = CFM_COLOR|CFM_BOLD|CFM_SPACING|
8011 CFM_ITALIC|CFM_CHARSET|CFM_WEIGHT;
8012 pvdisplayf.dwEffects = CFE_ITALIC;
8013 pvdisplayf.wWeight = 2;
8014 pvdisplayf.sSpacing = 10;
8015 pvdisplayf.crTextColor = RGB(0,0,0);
8018 SendMessage(PvDisplay, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
8019 (LPARAM) &pvdisplayf);
8023 enginenamf.cbSize = sizeof(CHARFORMAT);
8024 enginenamf.bCharSet = "Arial Black";
8025 enginenamf.dwMask = CFM_BOLD;
8026 enginenamf.crTextColor = RGB(0,0,0);
8027 enginenamf.dwEffects = CFE_BOLD;
8029 SendMessage(Name, EM_SETCHARFORMAT, (WPARAM) SCF_ALL,
8030 (LPARAM) &enginenamf);
8033 /* dirty dirty code: use buffer. I can use EM_*. If i have more time ...
8034 * WriteText is allso better. If War Room comes (2 engine window) i will
8035 * change to good code. At the moment we work with dirty buffers
8040 AnalysisPopUp(pv, depth, nps, nodes, score, move_nr, time, state)
8055 COLORREF NamePonder = RGB(255, 132, 132); /* opponent/ponder on move */
8056 COLORREF NameColor = RGB(171,216,173); /* we are on move */
8057 COLORREF NameAnalyze = RGB(148, 148, 248); /* Analyze anf FileanalyzeMode */
8058 COLORREF NameObserve = RGB(241, 248, 116); /* ICS observe */
8061 static char last_pv[MSG_SIZ];
8062 char pv_output[16384];
8063 static int last_move;
8064 static int counter, last_line;
8065 pvDisplay = GetDlgItem(analysisDialog, OPT_AnalysisText);
8066 Name = GetDlgItem(analysisDialog, OPT_engineName);
8067 engineStart = GetDlgItem(analysisDialog, OPT_engineStart);
8069 if (!analysisDialog) {
8070 lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);
8071 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),
8072 0, (DLGPROC)lpProc);
8073 FreeProcInstance(lpProc);
8074 SetEngineRoomFonts();
8075 /* engine button don't support icsMode */
8076 if (appData.icsActive) {
8077 SetDlgItemText(analysisDialog, OPT_engineStart, "Disable");
8078 SetDlgItemText(analysisDialog, OPT_engineMove, "Disable");
8080 SetDlgItemText(analysisDialog, OPT_engineStart, "Start Engine");
8081 SetDlgItemText(analysisDialog, OPT_engineMove, "Move!");
8083 /* if state 5 we have a WB bootup init */
8085 DisplayAnalysis(0,0);
8086 SendDlgItemMessage(analysisDialog, OPT_AnalysisText,
8087 LB_ADDSTRING, (WPARAM) 0,
8088 (LPARAM) "ready for play");
8089 //SetDlgItemText(analysisDialog, OPT_AnalysisText, "ready for play");
8094 /* check if core part say no support for stat line */
8095 /* only if new game or so */
8096 if (appData.engineStatLine == TRUE) {
8097 CheckDlgButton(analysisDialog, OPT_engineStatLine, BST_CHECKED);
8098 SetDlgItemText(analysisDialog, OPT_engineMoveNr, "disable");
8099 } else if (appData.engineStatLine == FALSE) {
8100 CheckDlgButton(analysisDialog, OPT_engineStatLine, BST_UNCHECKED);
8103 /* new move - begin from 0 */
8104 if (last_move != currentMove) {
8105 SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_RESETCONTENT, 0, 0);
8106 last_move = currentMove;
8110 /* Set engine button to engine state */
8111 if (pv[0] != NULLCHAR) {
8112 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8113 if (first.analyzing == TRUE) {
8114 SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
8117 } else if (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
8118 if (first.maybeThinking) {
8119 SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
8123 /* colorize EngineName - really check every action? */
8124 /* If we fast yes, think about build a own function
8125 * and set it on GameModeEvent()
8129 SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameColor);
8132 SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NamePonder);
8135 SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameAnalyze);
8138 SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameObserve);
8142 if (pv[0] != NULLCHAR) {
8143 strcpy(pv_output, time);
8144 strcat(pv_output, pv);
8145 strcpy(engineRoom[counter].mainline, pv_output);
8150 for (i = 0; i <= counter; i++) {
8152 strcpy(pv_output, engineRoom[i].mainline);
8153 strcat(pv_output, "\r\n");
8154 } else if (i == counter) {
8155 strcat(pv_output, engineRoom[i].mainline);
8157 strcat(pv_output, engineRoom[i].mainline);
8158 strcat(pv_output, "\r\n");
8161 //SetDlgItemText(analysisDialog, OPT_AnalysisText, pv_output);
8165 SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_ADDSTRING, (WPARAM) 0, (LPARAM) pv_output);
8168 SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_SETSEL, 0, -1);
8169 SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_SETSEL, counter, counter-1);
8171 SendMessage(pvDisplay, WM_VSCROLL, SB_BOTTOM, 0);
8174 SetDlgItemText(analysisDialog, OPT_engineDepth, depth);
8175 SetDlgItemText(analysisDialog, OPT_engineNPS, nps);
8176 SetDlgItemText(analysisDialog, OPT_engineNodes, nodes);
8177 SetDlgItemText(analysisDialog, OPT_engineScore, score);
8178 SetDlgItemText(analysisDialog, OPT_engineMoveNr, move_nr);
8179 SetDlgItemText(analysisDialog, OPT_engineTime, time);
8180 SetDlgItemText(analysisDialog, OPT_engineHash, "reserved");
8181 SetDlgItemText(analysisDialog, OPT_egtb, "reserved");
8183 SetDlgItemText(analysisDialog, OPT_engineName, "no support");
8184 if (appData.AnalysisWindow) {
8185 /* We need an other window and a *queue* for each engine to
8188 appData.AnalysisWindow = FALSE;
8189 SetDlgItemText(analysisDialog, OPT_AnalysisText,
8190 "Sorry, no Support. Please wait for a coming Engine War Room !");
8191 /* take TourneyMode to block user action ;-) */
8192 if (!appData.engineTourneyMode) appData.engineTourneyMode = TRUE;
8195 /* We don't need this because we have only 1 engine
8196 * and set on start should be enought, but for tests
8197 * it's enable at the moment
8199 SetDlgItemText(analysisDialog, OPT_engineName, first.tidy);
8202 strcpy(last_pv, pv);
8203 if (analysisDialogUp != TRUE) {
8204 analysisDialogUp = TRUE;
8205 ShowWindow(analysisDialog, SW_SHOW);
8212 if (analysisDialog) {
8213 ShowWindow(analysisDialog, SW_HIDE);
8215 analysisDialogUp = FALSE;
8220 SetHighlights(int fromX, int fromY, int toX, int toY)
8222 highlightInfo.sq[0].x = fromX;
8223 highlightInfo.sq[0].y = fromY;
8224 highlightInfo.sq[1].x = toX;
8225 highlightInfo.sq[1].y = toY;
8231 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
8232 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
8236 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
8238 premoveHighlightInfo.sq[0].x = fromX;
8239 premoveHighlightInfo.sq[0].y = fromY;
8240 premoveHighlightInfo.sq[1].x = toX;
8241 premoveHighlightInfo.sq[1].y = toY;
8245 ClearPremoveHighlights()
8247 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
8248 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
8254 if (saveSettingsOnExit) SaveSettings(settingsFileName);
8255 DeleteClipboardTempFiles();
8261 if (IsIconic(hwndMain))
8262 ShowWindow(hwndMain, SW_RESTORE);
8264 SetActiveWindow(hwndMain);
8268 * Prototypes for animation support routines
8270 static void ScreenSquare(int column, int row, POINT * pt);
8271 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
8272 POINT frames[], int * nFrames);
8278 AnimateMove(board, fromX, fromY, toX, toY)
8286 POINT start, finish, mid;
8287 POINT frames[kFactor * 2 + 1];
8290 if (!appData.animate) return;
8291 if (doingSizing) return;
8292 if (fromY < 0 || fromX < 0) return;
8293 piece = board[fromY][fromX];
8294 if (piece >= EmptySquare) return;
8296 ScreenSquare(fromX, fromY, &start);
8297 ScreenSquare(toX, toY, &finish);
8299 /* All pieces except knights move in straight line */
8300 if (piece != WhiteKnight && piece != BlackKnight) {
8301 mid.x = start.x + (finish.x - start.x) / 2;
8302 mid.y = start.y + (finish.y - start.y) / 2;
8304 /* Knight: make diagonal movement then straight */
8305 if (abs(toY - fromY) < abs(toX - fromX)) {
8306 mid.x = start.x + (finish.x - start.x) / 2;
8310 mid.y = start.y + (finish.y - start.y) / 2;
8314 /* Don't use as many frames for very short moves */
8315 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8316 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8318 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8320 animInfo.from.x = fromX;
8321 animInfo.from.y = fromY;
8322 animInfo.to.x = toX;
8323 animInfo.to.y = toY;
8324 animInfo.lastpos = start;
8325 animInfo.piece = piece;
8326 for (n = 0; n < nFrames; n++) {
8327 animInfo.pos = frames[n];
8328 DrawPosition(FALSE, NULL);
8329 animInfo.lastpos = animInfo.pos;
8330 Sleep(appData.animSpeed);
8332 animInfo.pos = finish;
8333 DrawPosition(FALSE, NULL);
8334 animInfo.piece = EmptySquare;
8337 /* Convert board position to corner of screen rect and color */
8340 ScreenSquare(column, row, pt)
8341 int column; int row; POINT * pt;
8344 pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
8345 pt->y = lineGap + row * (squareSize + lineGap);
8347 pt->x = lineGap + column * (squareSize + lineGap);
8348 pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
8352 /* Generate a series of frame coords from start->mid->finish.
8353 The movement rate doubles until the half way point is
8354 reached, then halves back down to the final destination,
8355 which gives a nice slow in/out effect. The algorithmn
8356 may seem to generate too many intermediates for short
8357 moves, but remember that the purpose is to attract the
8358 viewers attention to the piece about to be moved and
8359 then to where it ends up. Too few frames would be less
8363 Tween(start, mid, finish, factor, frames, nFrames)
8364 POINT * start; POINT * mid;
8365 POINT * finish; int factor;
8366 POINT frames[]; int * nFrames;
8368 int n, fraction = 1, count = 0;
8370 /* Slow in, stepping 1/16th, then 1/8th, ... */
8371 for (n = 0; n < factor; n++)
8373 for (n = 0; n < factor; n++) {
8374 frames[count].x = start->x + (mid->x - start->x) / fraction;
8375 frames[count].y = start->y + (mid->y - start->y) / fraction;
8377 fraction = fraction / 2;
8381 frames[count] = *mid;
8384 /* Slow out, stepping 1/2, then 1/4, ... */
8386 for (n = 0; n < factor; n++) {
8387 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8388 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8390 fraction = fraction * 2;
8396 HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
8398 /* Currently not implemented in WinBoard */