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 * ------------------------------------------------------------------------
80 #include "wgamelist.h"
81 #include "wedittags.h"
88 POINT pos; /* window coordinates of current pos */
89 POINT lastpos; /* window coordinates of last pos - used for clipping */
90 POINT from; /* board coordinates of the piece's orig pos */
91 POINT to; /* board coordinates of the piece's new pos */
94 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
97 POINT start; /* window coordinates of start pos */
98 POINT pos; /* window coordinates of current pos */
99 POINT lastpos; /* window coordinates of last pos - used for clipping */
100 POINT from; /* board coordinates of the piece's orig pos */
103 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
106 POINT sq[2]; /* board coordinates of from, to squares */
109 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
110 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
112 /* Window class names */
113 char szAppName[] = "WinBoard";
114 char szConsoleName[] = "WBConsole";
117 char szTitle[] = "WinBoard";
118 char szConsoleTitle[] = "ICS Interaction";
121 char *settingsFileName;
122 BOOLEAN saveSettingsOnExit;
123 char installDir[MSG_SIZ];
126 BOOLEAN chessProgram;
127 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;
128 static int squareSize, lineGap;
129 static int winWidth, winHeight;
130 static RECT messageRect, whiteRect, blackRect;
131 static char messageText[MESSAGE_TEXT_MAX];
132 static int clockTimerEvent = 0;
133 static int loadGameTimerEvent = 0;
134 static int analysisTimerEvent = 0;
135 static DelayedEventCallback delayedTimerCallback;
136 static int delayedTimerEvent = 0;
137 static int buttonCount = 2;
138 char *icsTextMenuString;
140 char *firstChessProgramNames;
141 char *secondChessProgramNames;
143 #define ARG_MAX 20000
145 #define PALETTESIZE 256
147 HINSTANCE hInst; /* current instance */
148 HWND hwndMain = NULL; /* root window*/
149 HWND hwndConsole = NULL;
150 BOOLEAN alwaysOnTop = FALSE;
152 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
153 blackPieceColor, highlightSquareColor, premoveHighlightColor;
155 ColorClass currentColorClass;
157 HWND hCommPort = NULL; /* currently open comm port */
158 static HWND hwndPause; /* pause button */
159 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];
160 static HBRUSH lightSquareBrush, darkSquareBrush,
161 whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;
162 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];
163 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];
164 static HPEN gridPen = NULL;
165 static HPEN highlightPen = NULL;
166 static HPEN premovePen = NULL;
167 static NPLOGPALETTE pLogPal;
168 static BOOL paletteChanged = FALSE;
169 static HICON iconWhite, iconBlack, iconCurrent;
170 static int doingSizing = FALSE;
171 static int lastSizing = 0;
172 static int prevStderrPort;
174 #if __GNUC__ && !defined(_winmajor)
175 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
177 #define oldDialog (_winmajor < 4)
180 char *defaultTextAttribs[] =
182 COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,
183 COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,
193 int cliWidth, cliHeight;
196 SizeInfo sizeInfo[] =
198 { "tiny", 21, 0, 1, 1, 0, 0 },
199 { "teeny", 25, 1, 1, 1, 0, 0 },
200 { "dinky", 29, 1, 1, 1, 0, 0 },
201 { "petite", 33, 1, 1, 1, 0, 0 },
202 { "slim", 37, 2, 1, 0, 0, 0 },
203 { "small", 40, 2, 1, 0, 0, 0 },
204 { "mediocre", 45, 2, 1, 0, 0, 0 },
205 { "middling", 49, 2, 0, 0, 0, 0 },
206 { "average", 54, 2, 0, 0, 0, 0 },
207 { "moderate", 58, 3, 0, 0, 0, 0 },
208 { "medium", 64, 3, 0, 0, 0, 0 },
209 { "bulky", 72, 3, 0, 0, 0, 0 },
210 { "large", 80, 3, 0, 0, 0, 0 },
211 { "big", 87, 3, 0, 0, 0, 0 },
212 { "huge", 95, 3, 0, 0, 0, 0 },
213 { "giant", 108, 3, 0, 0, 0, 0 },
214 { "colossal", 116, 4, 0, 0, 0, 0 },
215 { "titanic", 129, 4, 0, 0, 0, 0 },
216 { NULL, 0, 0, 0, 0, 0, 0 }
219 #define MF(x) {x, {0, }, {0, }, 0}
220 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
222 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY),
223 MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),
224 MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },
225 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY),
226 MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),
227 MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },
228 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),
229 MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),
230 MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },
231 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),
232 MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),
233 MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },
234 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),
235 MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),
236 MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },
237 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),
238 MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),
239 MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },
240 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),
241 MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),
242 MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },
243 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),
244 MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),
245 MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },
246 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),
247 MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),
248 MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },
249 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),
250 MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),
251 MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },
252 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),
253 MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),
254 MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },
255 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),
256 MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),
257 MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },
258 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),
259 MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),
260 MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },
261 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),
262 MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),
263 MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },
264 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),
265 MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),
266 MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },
267 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),
268 MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),
269 MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },
270 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),
271 MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),
272 MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },
273 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),
274 MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),
275 MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },
278 MyFont *font[NUM_SIZES][NUM_FONTS];
287 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
290 MyButtonDesc buttonDesc[N_BUTTONS] =
292 {"<<", IDM_ToStart, NULL, NULL},
293 {"<", IDM_Backward, NULL, NULL},
294 {"P", IDM_Pause, NULL, NULL},
295 {">", IDM_Forward, NULL, NULL},
296 {">>", IDM_ToEnd, NULL, NULL},
299 int tinyLayout = 0, smallLayout = 0;
300 #define MENU_BAR_ITEMS 6
301 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
302 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
303 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
307 MySound sounds[(int)NSoundClasses];
308 MyTextAttribs textAttribs[(int)NColorClasses];
310 MyColorizeAttribs colorizeAttribs[] = {
311 { (COLORREF)0, 0, "Shout Text" },
312 { (COLORREF)0, 0, "SShout/CShout" },
313 { (COLORREF)0, 0, "Channel 1 Text" },
314 { (COLORREF)0, 0, "Channel Text" },
315 { (COLORREF)0, 0, "Kibitz Text" },
316 { (COLORREF)0, 0, "Tell Text" },
317 { (COLORREF)0, 0, "Challenge Text" },
318 { (COLORREF)0, 0, "Request Text" },
319 { (COLORREF)0, 0, "Seek Text" },
320 { (COLORREF)0, 0, "Normal Text" },
321 { (COLORREF)0, 0, "None" }
326 static char *commentTitle;
327 static char *commentText;
328 static int commentIndex;
329 static Boolean editComment = FALSE;
330 HWND commentDialog = NULL;
331 BOOLEAN commentDialogUp = FALSE;
332 static int commentX, commentY, commentH, commentW;
334 static char *analysisTitle;
335 static char *analysisText;
336 HWND analysisDialog = NULL;
337 BOOLEAN analysisDialogUp = FALSE;
338 static int analysisX, analysisY, analysisH, analysisW;
340 char errorTitle[MSG_SIZ];
341 char errorMessage[2*MSG_SIZ];
342 HWND errorDialog = NULL;
343 BOOLEAN moveErrorMessageUp = FALSE;
344 BOOLEAN consoleEcho = TRUE;
345 CHARFORMAT consoleCF;
346 COLORREF consoleBackgroundColor;
348 char *programVersion;
363 SOCKET sock2; /* stderr socket for OpenRcmd */
366 #define INPUT_SOURCE_BUF_SIZE 4096
368 typedef struct _InputSource {
375 char buf[INPUT_SOURCE_BUF_SIZE];
380 struct _InputSource *second; /* for stderr thread on CPRcmd */
384 InputSource *consoleInputSource;
389 VOID ConsoleOutput(char* data, int length, int forceVisible);
390 VOID ConsoleCreate();
392 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
393 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
394 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
395 VOID ParseCommSettings(char *arg, DCB *dcb);
397 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
398 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
399 void ParseIcsTextMenu(char *icsTextMenuString);
400 VOID PopUpMoveDialog(char firstchar);
401 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
404 * Setting "frozen" should disable all user input other than deleting
405 * the window. We do this while engines are initializing themselves.
407 static int frozen = 0;
408 static int oldMenuItemState[MENU_BAR_ITEMS];
416 hmenu = GetMenu(hwndMain);
417 for (i=0; i<MENU_BAR_ITEMS; i++) {
418 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
420 DrawMenuBar(hwndMain);
423 /* Undo a FreezeUI */
431 hmenu = GetMenu(hwndMain);
432 for (i=0; i<MENU_BAR_ITEMS; i++) {
433 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
435 DrawMenuBar(hwndMain);
438 /*---------------------------------------------------------------------------*\
442 \*---------------------------------------------------------------------------*/
445 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
446 LPSTR lpCmdLine, int nCmdShow)
449 HANDLE hAccelMain, hAccelNoAlt;
453 LoadLibrary("RICHED32.DLL");
454 consoleCF.cbSize = sizeof(CHARFORMAT);
456 if (!InitApplication(hInstance)) {
459 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
463 hAccelMain = LoadAccelerators (hInstance, szAppName);
464 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
466 /* Acquire and dispatch messages until a WM_QUIT message is received. */
468 while (GetMessage(&msg, /* message structure */
469 NULL, /* handle of window receiving the message */
470 0, /* lowest message to examine */
471 0)) /* highest message to examine */
473 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
474 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
475 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
476 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
477 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&
478 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
479 TranslateMessage(&msg); /* Translates virtual key codes */
480 DispatchMessage(&msg); /* Dispatches message to window */
485 return (msg.wParam); /* Returns the value from PostQuitMessage */
488 /*---------------------------------------------------------------------------*\
490 * Initialization functions
492 \*---------------------------------------------------------------------------*/
495 InitApplication(HINSTANCE hInstance)
499 /* Fill in window class structure with parameters that describe the */
502 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
503 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
504 wc.cbClsExtra = 0; /* No per-class extra data. */
505 wc.cbWndExtra = 0; /* No per-window extra data. */
506 wc.hInstance = hInstance; /* Owner of this class */
507 wc.hIcon = LoadIcon(hInstance, "icon_white");
508 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
509 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
510 wc.lpszMenuName = szAppName; /* Menu name from .RC */
511 wc.lpszClassName = szAppName; /* Name to register as */
513 /* Register the window class and return success/failure code. */
514 if (!RegisterClass(&wc)) return FALSE;
516 wc.style = CS_HREDRAW | CS_VREDRAW;
517 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
519 wc.cbWndExtra = DLGWINDOWEXTRA;
520 wc.hInstance = hInstance;
521 wc.hIcon = LoadIcon(hInstance, "icon_white");
522 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
523 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
524 wc.lpszMenuName = NULL;
525 wc.lpszClassName = szConsoleName;
527 if (!RegisterClass(&wc)) return FALSE;
532 /* Set by InitInstance, used by EnsureOnScreen */
533 int screenHeight, screenWidth;
536 EnsureOnScreen(int *x, int *y)
538 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
539 if (*x > screenWidth - 32) *x = 0;
540 if (*y > screenHeight - 32) *y = 0;
544 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
546 HWND hwnd; /* Main window handle. */
551 hInst = hInstance; /* Store instance handle in our global variable */
553 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
554 *filepart = NULLCHAR;
556 GetCurrentDirectory(MSG_SIZ, installDir);
558 InitAppData(lpCmdLine); /* Get run-time parameters */
559 if (appData.debugMode) {
560 debugFP = fopen("winboard.debug", "w");
561 setbuf(debugFP, NULL);
566 /* Create a main window for this application instance. */
567 hwnd = CreateWindow(szAppName, szTitle,
568 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
569 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
570 NULL, NULL, hInstance, NULL);
573 /* If window could not be created, return "failure" */
578 iconWhite = LoadIcon(hInstance, "icon_white");
579 iconBlack = LoadIcon(hInstance, "icon_black");
580 iconCurrent = iconWhite;
582 screenHeight = GetSystemMetrics(SM_CYSCREEN);
583 screenWidth = GetSystemMetrics(SM_CXSCREEN);
584 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
585 /* Compute window size for each board size, and use the largest
586 size that fits on this screen as the default. */
587 InitDrawingSizes((BoardSize)ibs, 0);
588 if (boardSize == (BoardSize)-1 &&
589 winHeight <= screenHeight && winWidth <= screenWidth) {
590 boardSize = (BoardSize)ibs;
593 InitDrawingSizes(boardSize, 0);
595 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
599 /* Make the window visible; update its client area; and return "success" */
600 EnsureOnScreen(&boardX, &boardY);
601 wp.length = sizeof(WINDOWPLACEMENT);
603 wp.showCmd = nCmdShow;
604 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
605 wp.rcNormalPosition.left = boardX;
606 wp.rcNormalPosition.right = boardX + winWidth;
607 wp.rcNormalPosition.top = boardY;
608 wp.rcNormalPosition.bottom = boardY + winHeight;
609 SetWindowPlacement(hwndMain, &wp);
611 SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
612 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
615 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
616 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
618 ShowWindow(hwndConsole, nCmdShow);
628 ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone,
629 ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,
638 String *pString; // ArgString
640 float *pFloat; // ArgFloat
641 Boolean *pBoolean; // ArgBoolean
642 COLORREF *pColor; // ArgColor
643 ColorClass cc; // ArgAttribs
644 String *pFilename; // ArgFilename
645 BoardSize *pBoardSize; // ArgBoardSize
646 int whichFont; // ArgFont
647 DCB *pDCB; // ArgCommSettings
648 String *pFilename; // ArgSettingsFilename
656 ArgDescriptor argDescriptors[] = {
657 /* positional arguments */
658 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
659 { "", ArgNone, NULL },
660 /* keyword arguments */
661 { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },
662 { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },
663 { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },
664 { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },
665 { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },
666 { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },
667 { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },
668 { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },
669 { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },
670 { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },
671 { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },
672 { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },
673 { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },
674 { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },
675 { "initString", ArgString, (LPVOID) &appData.initString, FALSE },
676 { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },
677 { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },
678 { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,
680 { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,
682 { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,
684 { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },
685 { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,
687 { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },
688 { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },
689 { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },
690 { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
691 { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
692 { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },
693 { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },
694 { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
695 { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
696 { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },
697 { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },
698 { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },
699 { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },
700 { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
701 { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
702 { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
703 { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
704 /*!!bitmapDirectory?*/
705 { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
706 { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
707 { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
708 { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
709 { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },
710 { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },
711 { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },
712 { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },
713 { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },
714 { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },
715 { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },
716 { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },
717 { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
718 { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
719 { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },
720 { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },
721 { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },
722 { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },
723 { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
724 { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
725 { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
726 { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
727 { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
728 { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
729 { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },
730 { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },
731 { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
732 { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
733 { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },
734 { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },
735 { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },
736 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
737 { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
738 { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
739 { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
740 { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },
741 { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },
742 { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },
743 { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },
744 { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
745 { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
746 { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
747 { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
748 { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
749 { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
750 { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
751 { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
752 { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },
753 { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },
754 { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
755 { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
756 { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },
757 { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },
758 { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },
759 { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },
760 { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
761 { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
762 { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },
763 { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },
764 { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
765 { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
766 { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },
767 { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },
768 { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
769 { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
770 { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },
771 { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },
772 { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
773 { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
774 { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },
775 { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },
776 { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
777 { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
778 { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },
779 { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },
780 { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
781 { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
782 { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },
783 { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },
784 { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
785 { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
786 { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },
787 { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },
788 { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
789 { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
790 { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },
791 { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },
792 { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
793 { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
794 { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },
795 { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },
796 { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
797 { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
798 { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors,
799 FALSE }, /* only so that old WinBoard.ini files from betas can be read */
800 { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },
801 { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },
802 { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },
803 { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },
804 { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },
805 { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },
806 { "boardSize", ArgBoardSize, (LPVOID) &boardSize,
807 TRUE }, /* must come after all fonts */
808 { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },
809 { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,
810 FALSE }, /* historical; kept only so old winboard.ini files will parse */
811 { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },
812 { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },
813 { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
814 { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
815 { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },
816 { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },
817 { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
818 { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
819 { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },
820 { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },
821 { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
822 { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
823 { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },
824 { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },
825 { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
826 { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
827 { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },
828 { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },
829 { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
830 { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
831 { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },
832 { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },
833 { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
834 { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
835 { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },
836 { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },
837 { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
838 { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
840 { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
841 { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
843 { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },
844 { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
845 { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
846 { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
847 { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },
848 { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },
849 { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
850 { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
851 { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },
852 { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },
853 { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
854 { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
855 { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },
856 { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },
857 { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
858 { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
859 { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },
860 { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },
861 { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
862 { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
863 { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },
864 { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },
865 { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },
866 { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },
867 { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },
868 { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },
869 { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
870 { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
871 { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },
872 { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },
873 { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },
874 { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
875 { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
876 { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },
877 { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},
878 { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},
879 { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
880 { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
881 { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},
882 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
883 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
884 { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },
885 { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
886 { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
887 { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },
888 { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },
889 { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },
890 { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },
891 { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },
892 { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },
893 { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },
894 { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
895 { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
896 { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },
897 { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },
898 { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
899 { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
900 { "highlightLastMove", ArgBoolean,
901 (LPVOID) &appData.highlightLastMove, TRUE },
902 { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },
903 { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
904 { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
905 { "highlightDragging", ArgBoolean,
906 (LPVOID) &appData.highlightDragging, TRUE },
907 { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },
908 { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
909 { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
910 { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },
911 { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },
912 { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
913 { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
914 { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },
915 { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },
916 { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },
917 { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },
918 { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },
919 { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },
920 { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },
921 { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },
922 { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },
923 { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },
924 { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },
925 { "soundShout", ArgFilename,
926 (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },
927 { "soundSShout", ArgFilename,
928 (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },
929 { "soundChannel1", ArgFilename,
930 (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },
931 { "soundChannel", ArgFilename,
932 (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },
933 { "soundKibitz", ArgFilename,
934 (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },
935 { "soundTell", ArgFilename,
936 (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },
937 { "soundChallenge", ArgFilename,
938 (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },
939 { "soundRequest", ArgFilename,
940 (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },
941 { "soundSeek", ArgFilename,
942 (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },
943 { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },
944 { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },
945 { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },
946 { "soundIcsLoss", ArgFilename,
947 (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },
948 { "soundIcsDraw", ArgFilename,
949 (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },
950 { "soundIcsUnfinished", ArgFilename,
951 (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},
952 { "soundIcsAlarm", ArgFilename,
953 (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },
954 { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },
955 { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },
956 { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
957 { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
958 { "reuseChessPrograms", ArgBoolean,
959 (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */
960 { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },
961 { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },
962 { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
963 { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
964 { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },
965 { "x", ArgInt, (LPVOID) &boardX, TRUE },
966 { "y", ArgInt, (LPVOID) &boardY, TRUE },
967 { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },
968 { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },
969 { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },
970 { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },
971 { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },
972 { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },
973 { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },
974 { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },
975 { "commentX", ArgInt, (LPVOID) &commentX, TRUE },
976 { "commentY", ArgInt, (LPVOID) &commentY, TRUE },
977 { "commentW", ArgInt, (LPVOID) &commentW, TRUE },
978 { "commentH", ArgInt, (LPVOID) &commentH, TRUE },
979 { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },
980 { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },
981 { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },
982 { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },
983 { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },
984 { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },
985 { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },
986 { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },
987 { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
988 { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
989 { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },
990 { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },
991 { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },
992 { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },
993 { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },
994 { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },
995 { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },
996 { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,
998 { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,
1000 { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1001 { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1002 { "variant", ArgString, (LPVOID) &appData.variant, FALSE },
1003 { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,
1005 { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,
1007 { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },
1008 { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },
1009 { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1010 { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1012 { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },
1013 { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },
1014 { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1015 { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1016 { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },
1017 { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },
1018 { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1019 { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1020 { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },
1021 { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },
1022 { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },
1023 { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },
1024 { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,
1026 { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },
1027 { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },
1028 { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },
1029 { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1030 { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1031 { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },
1032 { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,
1034 { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1035 { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1036 { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1037 { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },
1038 { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },
1039 { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },
1040 { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },
1041 { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1042 { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1043 { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },
1044 { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },
1045 { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1046 { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1047 { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },
1048 { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },
1049 { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },
1050 /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */
1051 { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },
1053 { NULL, ArgNone, NULL, FALSE }
1057 /* Kludge for indirection files on command line */
1058 char* lastIndirectionFilename;
1059 ArgDescriptor argDescriptorIndirection =
1060 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };
1064 ExitArgError(char *msg, char *badArg)
1068 sprintf(buf, "%s %s", msg, badArg);
1069 DisplayFatalError(buf, 0, 2);
1073 /* Command line font name parser. NULL name means do nothing.
1074 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
1075 For backward compatibility, syntax without the colon is also
1076 accepted, but font names with digits in them won't work in that case.
1079 ParseFontName(char *name, MyFontParams *mfp)
1082 if (name == NULL) return;
1086 if (q - p >= sizeof(mfp->faceName))
1087 ExitArgError("Font name too long:", name);
1088 memcpy(mfp->faceName, p, q - p);
1089 mfp->faceName[q - p] = NULLCHAR;
1093 while (*p && !isdigit(*p)) {
1095 if (q - mfp->faceName >= sizeof(mfp->faceName))
1096 ExitArgError("Font name too long:", name);
1098 while (q > mfp->faceName && q[-1] == ' ') q--;
1101 if (!*p) ExitArgError("Font point size missing:", name);
1102 mfp->pointSize = (float) atof(p);
1103 mfp->bold = (strchr(p, 'b') != NULL);
1104 mfp->italic = (strchr(p, 'i') != NULL);
1105 mfp->underline = (strchr(p, 'u') != NULL);
1106 mfp->strikeout = (strchr(p, 's') != NULL);
1109 /* Color name parser.
1110 X version accepts X color names, but this one
1111 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
1113 ParseColorName(char *name)
1115 int red, green, blue, count;
1118 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
1120 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
1121 &red, &green, &blue);
1124 sprintf(buf, "Can't parse color name %s", name);
1125 DisplayError(buf, 0);
1126 return RGB(0, 0, 0);
1128 return PALETTERGB(red, green, blue);
1132 void ParseAttribs(COLORREF *color, int *effects, char* argValue)
1138 if (*e == 'b') eff |= CFE_BOLD;
1139 else if (*e == 'i') eff |= CFE_ITALIC;
1140 else if (*e == 'u') eff |= CFE_UNDERLINE;
1141 else if (*e == 's') eff |= CFE_STRIKEOUT;
1142 else if (*e == '#' || isdigit(*e)) break;
1146 *color = ParseColorName(e);
1151 ParseBoardSize(char *name)
1153 BoardSize bs = SizeTiny;
1154 while (sizeInfo[bs].name != NULL) {
1155 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;
1158 ExitArgError("Unrecognized board size value", name);
1159 return bs; /* not reached */
1164 StringGet(void *getClosure)
1166 char **p = (char **) getClosure;
1171 FileGet(void *getClosure)
1174 FILE* f = (FILE*) getClosure;
1183 /* Parse settings file named "name". If file found, return the
1184 full name in fullname and return TRUE; else return FALSE */
1186 ParseSettingsFile(char *name, char fullname[MSG_SIZ])
1191 if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {
1192 f = fopen(fullname, "r");
1194 ParseArgs(FileGet, f);
1203 ParseArgs(GetFunc get, void *cl)
1205 char argName[ARG_MAX];
1206 char argValue[ARG_MAX];
1216 while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);
1217 if (ch == NULLCHAR) break;
1219 /* Comment to end of line */
1221 while (ch != '\n' && ch != NULLCHAR) ch = get(cl);
1223 } else if (ch == '/' || ch == '-') {
1226 while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&
1227 ch != '\n' && ch != '\t') {
1233 for (ad = argDescriptors; ad->argName != NULL; ad++)
1234 if (strcmp(ad->argName, argName + 1) == 0) break;
1236 if (ad->argName == NULL)
1237 ExitArgError("Unrecognized argument", argName);
1239 } else if (ch == '@') {
1240 /* Indirection file */
1241 ad = &argDescriptorIndirection;
1244 /* Positional argument */
1245 ad = &argDescriptors[posarg++];
1246 strcpy(argName, ad->argName);
1249 if (ad->argType == ArgTrue) {
1250 *(Boolean *) ad->argLoc = TRUE;
1253 if (ad->argType == ArgFalse) {
1254 *(Boolean *) ad->argLoc = FALSE;
1258 while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);
1259 if (ch == NULLCHAR || ch == '\n') {
1260 ExitArgError("No value provided for argument", argName);
1264 // Quoting with { }. No characters have to (or can) be escaped.
1265 // Thus the string cannot contain a '}' character.
1285 } else if (ch == '\'' || ch == '"') {
1286 // Quoting with ' ' or " ", with \ as escape character.
1287 // Inconvenient for long strings that may contain Windows filenames.
1313 if (ad->argType == ArgFilename
1314 || ad->argType == ArgSettingsFilename) {
1320 ExitArgError("Incomplete \\ escape in value for", argName);
1344 for (i = 0; i < 3; i++) {
1345 if (ch >= '0' && ch <= '7') {
1346 octval = octval*8 + (ch - '0');
1353 *q++ = (char) octval;
1364 while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {
1371 switch (ad->argType) {
1373 *(int *) ad->argLoc = atoi(argValue);
1377 *(float *) ad->argLoc = (float) atof(argValue);
1382 *(char **) ad->argLoc = strdup(argValue);
1385 case ArgSettingsFilename:
1387 char fullname[MSG_SIZ];
1388 if (ParseSettingsFile(argValue, fullname)) {
1389 if (ad->argLoc != NULL) {
1390 *(char **) ad->argLoc = strdup(fullname);
1393 if (ad->argLoc != NULL) {
1395 ExitArgError("Failed to open indirection file", argValue);
1402 switch (argValue[0]) {
1405 *(Boolean *) ad->argLoc = TRUE;
1409 *(Boolean *) ad->argLoc = FALSE;
1412 ExitArgError("Unrecognized boolean argument value", argValue);
1418 *(COLORREF *)ad->argLoc = ParseColorName(argValue);
1422 ColorClass cc = (ColorClass)ad->argLoc;
1423 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);
1428 *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);
1432 ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);
1435 case ArgCommSettings:
1436 ParseCommSettings(argValue, &dcb);
1440 ExitArgError("Unrecognized argument", argValue);
1447 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
1449 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
1450 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
1453 lf->lfEscapement = 0;
1454 lf->lfOrientation = 0;
1455 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
1456 lf->lfItalic = mfp->italic;
1457 lf->lfUnderline = mfp->underline;
1458 lf->lfStrikeOut = mfp->strikeout;
1459 lf->lfCharSet = DEFAULT_CHARSET;
1460 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
1461 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
1462 lf->lfQuality = DEFAULT_QUALITY;
1463 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
1464 strcpy(lf->lfFaceName, mfp->faceName);
1468 CreateFontInMF(MyFont *mf)
1470 LFfromMFP(&mf->lf, &mf->mfp);
1471 if (mf->hf) DeleteObject(mf->hf);
1472 mf->hf = CreateFontIndirect(&mf->lf);
1476 SetDefaultTextAttribs()
1479 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1480 ParseAttribs(&textAttribs[cc].color,
1481 &textAttribs[cc].effects,
1482 defaultTextAttribs[cc]);
1491 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1492 textAttribs[cc].sound.name = strdup("");
1493 textAttribs[cc].sound.data = NULL;
1495 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1496 sounds[sc].name = strdup("");
1497 sounds[sc].data = NULL;
1499 sounds[(int)SoundBell].name = strdup(SOUND_BELL);
1507 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1508 MyLoadSound(&textAttribs[cc].sound);
1510 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1511 MyLoadSound(&sounds[sc]);
1516 InitAppData(LPSTR lpCmdLine)
1519 char buf[ARG_MAX], currDir[MSG_SIZ];
1522 programName = szAppName;
1524 /* Initialize to defaults */
1525 lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);
1526 darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);
1527 whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);
1528 blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);
1529 highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);
1530 premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);
1531 consoleBackgroundColor = ParseColorName(COLOR_BKGD);
1532 SetDefaultTextAttribs();
1534 appData.movesPerSession = MOVES_PER_SESSION;
1535 appData.initString = INIT_STRING;
1536 appData.secondInitString = INIT_STRING;
1537 appData.firstComputerString = COMPUTER_STRING;
1538 appData.secondComputerString = COMPUTER_STRING;
1539 appData.firstChessProgram = FIRST_CHESS_PROGRAM;
1540 appData.secondChessProgram = SECOND_CHESS_PROGRAM;
1541 appData.firstPlaysBlack = FALSE;
1542 appData.noChessProgram = FALSE;
1543 chessProgram = FALSE;
1544 appData.firstHost = FIRST_HOST;
1545 appData.secondHost = SECOND_HOST;
1546 appData.firstDirectory = FIRST_DIRECTORY;
1547 appData.secondDirectory = SECOND_DIRECTORY;
1548 appData.bitmapDirectory = "";
1549 appData.remoteShell = REMOTE_SHELL;
1550 appData.remoteUser = "";
1551 appData.timeDelay = TIME_DELAY;
1552 appData.timeControl = TIME_CONTROL;
1553 appData.timeIncrement = TIME_INCREMENT;
1554 appData.icsActive = FALSE;
1555 appData.icsHost = "";
1556 appData.icsPort = ICS_PORT;
1557 appData.icsCommPort = ICS_COMM_PORT;
1558 appData.icsLogon = ICS_LOGON;
1559 appData.icsHelper = "";
1560 appData.useTelnet = FALSE;
1561 appData.telnetProgram = TELNET_PROGRAM;
1562 appData.gateway = "";
1563 appData.loadGameFile = "";
1564 appData.loadGameIndex = 0;
1565 appData.saveGameFile = "";
1566 appData.autoSaveGames = FALSE;
1567 appData.loadPositionFile = "";
1568 appData.loadPositionIndex = 1;
1569 appData.savePositionFile = "";
1570 appData.matchMode = FALSE;
1571 appData.matchGames = 0;
1572 appData.monoMode = FALSE;
1573 appData.debugMode = FALSE;
1574 appData.clockMode = TRUE;
1575 boardSize = (BoardSize) -1; /* determine by screen size */
1576 appData.Iconic = FALSE; /*unused*/
1577 appData.searchTime = "";
1578 appData.searchDepth = 0;
1579 appData.showCoords = FALSE;
1580 appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/
1581 appData.autoCallFlag = FALSE;
1582 appData.flipView = FALSE;
1583 appData.autoFlipView = TRUE;
1584 appData.cmailGameName = "";
1585 appData.alwaysPromoteToQueen = FALSE;
1586 appData.oldSaveStyle = FALSE;
1587 appData.quietPlay = FALSE;
1588 appData.showThinking = FALSE;
1589 appData.ponderNextMove = TRUE;
1590 appData.periodicUpdates = TRUE;
1591 appData.popupExitMessage = TRUE;
1592 appData.popupMoveErrors = FALSE;
1593 appData.autoObserve = FALSE;
1594 appData.autoComment = FALSE;
1595 appData.animate = TRUE;
1596 appData.animSpeed = 10;
1597 appData.animateDragging = TRUE;
1598 appData.highlightLastMove = TRUE;
1599 appData.getMoveList = TRUE;
1600 appData.testLegality = TRUE;
1601 appData.premove = TRUE;
1602 appData.premoveWhite = FALSE;
1603 appData.premoveWhiteText = "";
1604 appData.premoveBlack = FALSE;
1605 appData.premoveBlackText = "";
1606 appData.icsAlarm = TRUE;
1607 appData.icsAlarmTime = 5000;
1608 appData.autoRaiseBoard = TRUE;
1609 appData.localLineEditing = TRUE;
1610 appData.colorize = TRUE;
1611 appData.reuseFirst = TRUE;
1612 appData.reuseSecond = TRUE;
1613 appData.blindfold = FALSE;
1614 appData.icsEngineAnalyze = FALSE;
1615 dcb.DCBlength = sizeof(DCB);
1616 dcb.BaudRate = 9600;
1618 dcb.fParity = FALSE;
1619 dcb.fOutxCtsFlow = FALSE;
1620 dcb.fOutxDsrFlow = FALSE;
1621 dcb.fDtrControl = DTR_CONTROL_ENABLE;
1622 dcb.fDsrSensitivity = FALSE;
1623 dcb.fTXContinueOnXoff = TRUE;
1627 dcb.fRtsControl = RTS_CONTROL_ENABLE;
1628 dcb.fAbortOnError = FALSE;
1629 /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */
1630 #if (defined(_MSC_VER) && _MSC_VER <= 1200)
1631 //dcb.wReserved = 0;
1636 dcb.Parity = SPACEPARITY;
1637 dcb.StopBits = ONESTOPBIT;
1638 settingsFileName = SETTINGS_FILE;
1639 saveSettingsOnExit = TRUE;
1640 boardX = CW_USEDEFAULT;
1641 boardY = CW_USEDEFAULT;
1642 consoleX = CW_USEDEFAULT;
1643 consoleY = CW_USEDEFAULT;
1644 consoleW = CW_USEDEFAULT;
1645 consoleH = CW_USEDEFAULT;
1646 analysisX = CW_USEDEFAULT;
1647 analysisY = CW_USEDEFAULT;
1648 analysisW = CW_USEDEFAULT;
1649 analysisH = CW_USEDEFAULT;
1650 commentX = CW_USEDEFAULT;
1651 commentY = CW_USEDEFAULT;
1652 commentW = CW_USEDEFAULT;
1653 commentH = CW_USEDEFAULT;
1654 editTagsX = CW_USEDEFAULT;
1655 editTagsY = CW_USEDEFAULT;
1656 editTagsW = CW_USEDEFAULT;
1657 editTagsH = CW_USEDEFAULT;
1658 gameListX = CW_USEDEFAULT;
1659 gameListY = CW_USEDEFAULT;
1660 gameListW = CW_USEDEFAULT;
1661 gameListH = CW_USEDEFAULT;
1662 icsTextMenuString = ICS_TEXT_MENU_DEFAULT;
1663 icsNames = ICS_NAMES;
1664 firstChessProgramNames = FCP_NAMES;
1665 secondChessProgramNames = SCP_NAMES;
1666 appData.initialMode = "";
1667 appData.variant = "normal";
1668 appData.firstProtocolVersion = PROTOVER;
1669 appData.secondProtocolVersion = PROTOVER;
1670 appData.showButtonBar = TRUE;
1672 appData.zippyTalk = ZIPPY_TALK;
1673 appData.zippyPlay = ZIPPY_PLAY;
1674 appData.zippyLines = ZIPPY_LINES;
1675 appData.zippyPinhead = ZIPPY_PINHEAD;
1676 appData.zippyPassword = ZIPPY_PASSWORD;
1677 appData.zippyPassword2 = ZIPPY_PASSWORD2;
1678 appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;
1679 appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;
1680 appData.zippyUseI = ZIPPY_USE_I;
1681 appData.zippyBughouse = ZIPPY_BUGHOUSE;
1682 appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;
1683 appData.zippyGameEnd = ZIPPY_GAME_END;
1684 appData.zippyGameStart = ZIPPY_GAME_START;
1685 appData.zippyAdjourn = ZIPPY_ADJOURN;
1686 appData.zippyAbort = ZIPPY_ABORT;
1687 appData.zippyVariants = ZIPPY_VARIANTS;
1688 appData.zippyMaxGames = ZIPPY_MAX_GAMES;
1689 appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;
1692 /* Point font array elements to structures and
1693 parse default font names */
1694 for (i=0; i<NUM_FONTS; i++) {
1695 for (j=0; j<NUM_SIZES; j++) {
1696 font[j][i] = &fontRec[j][i];
1697 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
1701 /* Parse default settings file if any */
1702 if (ParseSettingsFile(settingsFileName, buf)) {
1703 settingsFileName = strdup(buf);
1706 /* Parse command line */
1707 ParseArgs(StringGet, &lpCmdLine);
1709 /* Propagate options that affect others */
1710 if (appData.matchMode || appData.matchGames) chessProgram = TRUE;
1711 if (appData.icsActive || appData.noChessProgram) {
1712 chessProgram = FALSE; /* not local chess program mode */
1715 /* Open startup dialog if needed */
1716 if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||
1717 (appData.icsActive && *appData.icsHost == NULLCHAR) ||
1718 (chessProgram && (*appData.firstChessProgram == NULLCHAR ||
1719 *appData.secondChessProgram == NULLCHAR))) {
1722 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
1723 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
1724 FreeProcInstance(lpProc);
1727 /* Make sure save files land in the right (?) directory */
1728 if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {
1729 appData.saveGameFile = strdup(buf);
1731 if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {
1732 appData.savePositionFile = strdup(buf);
1735 /* Finish initialization for fonts and sounds */
1736 for (i=0; i<NUM_FONTS; i++) {
1737 for (j=0; j<NUM_SIZES; j++) {
1738 CreateFontInMF(font[j][i]);
1741 /* xboard, and older WinBoards, controlled the move sound with the
1742 appData.ringBellAfterMoves option. In the current WinBoard, we
1743 always turn the option on (so that the backend will call us),
1744 then let the user turn the sound off by setting it to silence if
1745 desired. To accommodate old winboard.ini files saved by old
1746 versions of WinBoard, we also turn off the sound if the option
1747 was initially set to false. */
1748 if (!appData.ringBellAfterMoves) {
1749 sounds[(int)SoundMove].name = strdup("");
1750 appData.ringBellAfterMoves = TRUE;
1752 GetCurrentDirectory(MSG_SIZ, currDir);
1753 SetCurrentDirectory(installDir);
1755 SetCurrentDirectory(currDir);
1757 p = icsTextMenuString;
1759 FILE* f = fopen(p + 1, "r");
1761 DisplayFatalError(p + 1, errno, 2);
1764 i = fread(buf, 1, sizeof(buf)-1, f);
1769 ParseIcsTextMenu(strdup(p));
1776 HMENU hmenu = GetMenu(hwndMain);
1778 (void) EnableMenuItem(hmenu, IDM_CommPort,
1779 MF_BYCOMMAND|((appData.icsActive &&
1780 *appData.icsCommPort != NULLCHAR) ?
1781 MF_ENABLED : MF_GRAYED));
1782 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
1783 MF_BYCOMMAND|(saveSettingsOnExit ?
1784 MF_CHECKED : MF_UNCHECKED));
1789 SaveSettings(char* name)
1796 if (!hwndMain) return;
1798 GetCurrentDirectory(MSG_SIZ, dir);
1799 SetCurrentDirectory(installDir);
1800 f = fopen(name, "w");
1801 SetCurrentDirectory(dir);
1803 DisplayError(name, errno);
1807 fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);
1809 fprintf(f, "; You can edit the values of options that are already set in this file,\n");
1810 fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");
1811 fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");
1814 wp.length = sizeof(WINDOWPLACEMENT);
1815 GetWindowPlacement(hwndMain, &wp);
1816 boardX = wp.rcNormalPosition.left;
1817 boardY = wp.rcNormalPosition.top;
1820 GetWindowPlacement(hwndConsole, &wp);
1821 consoleX = wp.rcNormalPosition.left;
1822 consoleY = wp.rcNormalPosition.top;
1823 consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1824 consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1827 if (analysisDialog) {
1828 GetWindowPlacement(analysisDialog, &wp);
1829 analysisX = wp.rcNormalPosition.left;
1830 analysisY = wp.rcNormalPosition.top;
1831 analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1832 analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1835 if (commentDialog) {
1836 GetWindowPlacement(commentDialog, &wp);
1837 commentX = wp.rcNormalPosition.left;
1838 commentY = wp.rcNormalPosition.top;
1839 commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1840 commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1843 if (editTagsDialog) {
1844 GetWindowPlacement(editTagsDialog, &wp);
1845 editTagsX = wp.rcNormalPosition.left;
1846 editTagsY = wp.rcNormalPosition.top;
1847 editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1848 editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1851 if (gameListDialog) {
1852 GetWindowPlacement(gameListDialog, &wp);
1853 gameListX = wp.rcNormalPosition.left;
1854 gameListY = wp.rcNormalPosition.top;
1855 gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1856 gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1859 for (ad = argDescriptors; ad->argName != NULL; ad++) {
1860 if (!ad->save) continue;
1861 switch (ad->argType) {
1864 char *p = *(char **)ad->argLoc;
1865 if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {
1866 /* Quote multiline values or \-containing values
1867 with { } if possible */
1868 fprintf(f, "/%s={%s}\n", ad->argName, p);
1870 /* Else quote with " " */
1871 fprintf(f, "/%s=\"", ad->argName);
1873 if (*p == '\n') fprintf(f, "\n");
1874 else if (*p == '\r') fprintf(f, "\\r");
1875 else if (*p == '\t') fprintf(f, "\\t");
1876 else if (*p == '\b') fprintf(f, "\\b");
1877 else if (*p == '\f') fprintf(f, "\\f");
1878 else if (*p < ' ') fprintf(f, "\\%03o", *p);
1879 else if (*p == '\"') fprintf(f, "\\\"");
1880 else if (*p == '\\') fprintf(f, "\\\\");
1889 fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);
1892 fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);
1895 fprintf(f, "/%s=%s\n", ad->argName,
1896 (*(Boolean *)ad->argLoc) ? "true" : "false");
1899 if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1902 if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1906 COLORREF color = *(COLORREF *)ad->argLoc;
1907 fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName,
1908 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1913 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
1914 fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,
1915 (ta->effects & CFE_BOLD) ? "b" : "",
1916 (ta->effects & CFE_ITALIC) ? "i" : "",
1917 (ta->effects & CFE_UNDERLINE) ? "u" : "",
1918 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
1919 (ta->effects) ? " " : "",
1920 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
1924 if (strchr(*(char **)ad->argLoc, '\"')) {
1925 fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);
1927 fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);
1931 fprintf(f, "/%s=%s\n", ad->argName,
1932 sizeInfo[*(BoardSize *)ad->argLoc].name);
1937 for (bs=0; bs<NUM_SIZES; bs++) {
1938 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
1939 fprintf(f, "/size=%s ", sizeInfo[bs].name);
1940 fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",
1941 ad->argName, mfp->faceName, mfp->pointSize,
1942 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
1943 mfp->bold ? "b" : "",
1944 mfp->italic ? "i" : "",
1945 mfp->underline ? "u" : "",
1946 mfp->strikeout ? "s" : "");
1950 case ArgCommSettings:
1951 PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);
1959 /*---------------------------------------------------------------------------*\
1961 * GDI board drawing routines
1963 \*---------------------------------------------------------------------------*/
1966 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
1970 sprintf(name, "%s%d%s", piece, squareSize, suffix);
1971 if (gameInfo.event &&
1972 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
1973 strcmp(name, "k80s") == 0) {
1974 strcpy(name, "tim");
1976 return LoadBitmap(hinst, name);
1980 /* Insert a color into the program's logical palette
1981 structure. This code assumes the given color is
1982 the result of the RGB or PALETTERGB macro, and it
1983 knows how those macros work (which is documented).
1986 InsertInPalette(COLORREF color)
1988 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
1990 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
1991 DisplayFatalError("Too many colors", 0, 1);
1992 pLogPal->palNumEntries--;
1996 pe->peFlags = (char) 0;
1997 pe->peRed = (char) (0xFF & color);
1998 pe->peGreen = (char) (0xFF & (color >> 8));
1999 pe->peBlue = (char) (0xFF & (color >> 16));
2007 if (pLogPal == NULL) {
2008 /* Allocate enough memory for a logical palette with
2009 * PALETTESIZE entries and set the size and version fields
2010 * of the logical palette structure.
2012 pLogPal = (NPLOGPALETTE)
2013 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
2014 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
2015 pLogPal->palVersion = 0x300;
2017 pLogPal->palNumEntries = 0;
2019 InsertInPalette(lightSquareColor);
2020 InsertInPalette(darkSquareColor);
2021 InsertInPalette(whitePieceColor);
2022 InsertInPalette(blackPieceColor);
2023 InsertInPalette(highlightSquareColor);
2024 InsertInPalette(premoveHighlightColor);
2026 /* create a logical color palette according the information
2027 * in the LOGPALETTE structure.
2029 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
2031 lightSquareBrush = CreateSolidBrush(lightSquareColor);
2032 darkSquareBrush = CreateSolidBrush(darkSquareColor);
2033 whitePieceBrush = CreateSolidBrush(whitePieceColor);
2034 blackPieceBrush = CreateSolidBrush(blackPieceColor);
2035 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
2040 BoardWidth(int boardSize)
2042 return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +
2043 BOARD_SIZE * sizeInfo[boardSize].squareSize;
2046 /* Respond to board resize by dragging edge */
2048 ResizeBoard(int newSizeX, int newSizeY, int flags)
2050 BoardSize newSize = NUM_SIZES - 1;
2051 static int recurse = 0;
2052 if (IsIconic(hwndMain)) return;
2053 if (recurse > 0) return;
2055 while (newSize > 0 &&
2056 (newSizeX < sizeInfo[newSize].cliWidth ||
2057 newSizeY < sizeInfo[newSize].cliHeight)) {
2060 boardSize = newSize;
2061 InitDrawingSizes(boardSize, flags);
2068 InitDrawingSizes(BoardSize boardSize, int flags)
2072 static int oldBoardSize = -1, oldTinyLayout = 0;
2074 SIZE clockSize, messageSize;
2078 HMENU hmenu = GetMenu(hwndMain);
2083 tinyLayout = sizeInfo[boardSize].tinyLayout;
2084 smallLayout = sizeInfo[boardSize].smallLayout;
2085 squareSize = sizeInfo[boardSize].squareSize;
2086 lineGap = sizeInfo[boardSize].lineGap;
2088 if (tinyLayout != oldTinyLayout) {
2089 long style = GetWindowLong(hwndMain, GWL_STYLE);
2091 style &= ~WS_SYSMENU;
2092 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
2093 "&Minimize\tCtrl+F4");
2095 style |= WS_SYSMENU;
2096 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
2098 SetWindowLong(hwndMain, GWL_STYLE, style);
2100 for (i=0; menuBarText[tinyLayout][i]; i++) {
2101 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
2102 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
2104 DrawMenuBar(hwndMain);
2107 boardWidth = BoardWidth(boardSize);
2109 /* Get text area sizes */
2110 hdc = GetDC(hwndMain);
2111 if (appData.clockMode) {
2112 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
2114 sprintf(buf, "White");
2116 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
2117 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
2118 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2119 str = "We only care about the height here";
2120 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
2121 SelectObject(hdc, oldFont);
2122 ReleaseDC(hwndMain, hdc);
2124 /* Compute where everything goes */
2125 whiteRect.left = OUTER_MARGIN;
2126 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
2127 whiteRect.top = OUTER_MARGIN;
2128 whiteRect.bottom = whiteRect.top + clockSize.cy;
2130 blackRect.left = whiteRect.right + INNER_MARGIN;
2131 blackRect.right = blackRect.left + boardWidth/2 - 1;
2132 blackRect.top = whiteRect.top;
2133 blackRect.bottom = whiteRect.bottom;
2135 messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;
2136 if (appData.showButtonBar) {
2137 messageRect.right = blackRect.right
2138 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
2140 messageRect.right = blackRect.right;
2142 messageRect.top = whiteRect.bottom + INNER_MARGIN;
2143 messageRect.bottom = messageRect.top + messageSize.cy;
2145 boardRect.left = whiteRect.left;
2146 boardRect.right = boardRect.left + boardWidth;
2147 boardRect.top = messageRect.bottom + INNER_MARGIN;
2148 boardRect.bottom = boardRect.top + boardWidth;
2150 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
2151 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
2152 winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
2153 winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
2154 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
2155 GetWindowRect(hwndMain, &wrect);
2156 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2157 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2158 /* compensate if menu bar wrapped */
2159 GetClientRect(hwndMain, &crect);
2160 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
2164 SetWindowPos(hwndMain, NULL,
2165 wrect.right - winWidth, wrect.bottom - winHeight,
2166 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2171 SetWindowPos(hwndMain, NULL,
2172 wrect.left, wrect.bottom - winHeight,
2173 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2176 case WMSZ_BOTTOMLEFT:
2178 SetWindowPos(hwndMain, NULL,
2179 wrect.right - winWidth, wrect.top,
2180 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2183 case WMSZ_BOTTOMRIGHT:
2187 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2188 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2193 for (i = 0; i < N_BUTTONS; i++) {
2194 if (buttonDesc[i].hwnd != NULL) {
2195 DestroyWindow(buttonDesc[i].hwnd);
2196 buttonDesc[i].hwnd = NULL;
2198 if (appData.showButtonBar) {
2199 buttonDesc[i].hwnd =
2200 CreateWindow("BUTTON", buttonDesc[i].label,
2201 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
2202 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
2203 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
2204 (HMENU) buttonDesc[i].id,
2205 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
2207 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
2208 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
2209 MAKELPARAM(FALSE, 0));
2211 if (buttonDesc[i].id == IDM_Pause)
2212 hwndPause = buttonDesc[i].hwnd;
2213 buttonDesc[i].wndproc = (WNDPROC)
2214 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
2217 if (gridPen != NULL) DeleteObject(gridPen);
2218 if (highlightPen != NULL) DeleteObject(highlightPen);
2219 if (premovePen != NULL) DeleteObject(premovePen);
2221 logbrush.lbStyle = BS_SOLID;
2222 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
2224 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2225 lineGap, &logbrush, 0, NULL);
2226 logbrush.lbColor = highlightSquareColor;
2228 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2229 lineGap, &logbrush, 0, NULL);
2231 logbrush.lbColor = premoveHighlightColor;
2233 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2234 lineGap, &logbrush, 0, NULL);
2236 for (i = 0; i < BOARD_SIZE + 1; i++) {
2237 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
2238 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;
2239 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
2240 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
2241 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
2242 BOARD_SIZE * (squareSize + lineGap);
2243 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =
2244 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +
2245 lineGap / 2 + (i * (squareSize + lineGap));
2246 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =
2247 boardRect.top + BOARD_SIZE * (squareSize + lineGap);
2248 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
2252 if (boardSize == oldBoardSize) return;
2253 oldBoardSize = boardSize;
2254 oldTinyLayout = tinyLayout;
2256 /* Load piece bitmaps for this board size */
2257 for (i=0; i<=2; i++) {
2258 for (piece = WhitePawn;
2259 (int) piece <= (int) WhiteKing;
2260 piece = (ChessSquare) ((int) piece + 1)) {
2261 if (pieceBitmap[i][piece] != NULL)
2262 DeleteObject(pieceBitmap[i][piece]);
2266 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
2267 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
2268 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
2269 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
2270 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
2271 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
2272 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
2273 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
2274 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
2275 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
2276 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
2277 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
2278 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
2279 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
2280 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
2281 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
2282 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
2283 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
2288 PieceBitmap(ChessSquare p, int kind)
2290 if ((int) p >= (int) BlackPawn)
2291 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
2293 return pieceBitmap[kind][(int) p];
2296 /***************************************************************/
2298 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
2299 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
2301 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
2302 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
2306 SquareToPos(int row, int column, int * x, int * y)
2309 *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
2310 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
2312 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
2313 *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
2318 DrawCoordsOnDC(HDC hdc)
2320 static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};
2321 static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};
2322 char str[2] = { NULLCHAR, NULLCHAR };
2323 int oldMode, oldAlign, x, y, start, i;
2327 if (!appData.showCoords)
2330 start = flipView ? 0 : 8;
2332 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
2333 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
2334 oldAlign = GetTextAlign(hdc);
2335 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
2337 y = boardRect.top + lineGap;
2338 x = boardRect.left + lineGap;
2340 SetTextAlign(hdc, TA_LEFT|TA_TOP);
2341 for (i = 0; i < 8; i++) {
2342 str[0] = files[start + i];
2343 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
2344 y += squareSize + lineGap;
2347 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
2348 for (i = 0; i < 8; i++) {
2349 str[0] = ranks[start + i];
2350 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
2351 x += squareSize + lineGap;
2354 SelectObject(hdc, oldBrush);
2355 SetBkMode(hdc, oldMode);
2356 SetTextAlign(hdc, oldAlign);
2357 SelectObject(hdc, oldFont);
2361 DrawGridOnDC(HDC hdc)
2366 oldPen = SelectObject(hdc, gridPen);
2367 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);
2368 SelectObject(hdc, oldPen);
2372 #define HIGHLIGHT_PEN 0
2373 #define PREMOVE_PEN 1
2376 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
2380 if (lineGap == 0) return;
2382 x1 = boardRect.left +
2383 lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);
2384 y1 = boardRect.top +
2385 lineGap/2 + y * (squareSize + lineGap);
2387 x1 = boardRect.left +
2388 lineGap/2 + x * (squareSize + lineGap);
2389 y1 = boardRect.top +
2390 lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);
2392 hPen = pen ? premovePen : highlightPen;
2393 oldPen = SelectObject(hdc, on ? hPen : gridPen);
2394 MoveToEx(hdc, x1, y1, NULL);
2395 LineTo(hdc, x1 + squareSize + lineGap, y1);
2396 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
2397 LineTo(hdc, x1, y1 + squareSize + lineGap);
2398 LineTo(hdc, x1, y1);
2399 SelectObject(hdc, oldPen);
2403 DrawHighlightsOnDC(HDC hdc)
2406 for (i=0; i<2; i++) {
2407 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
2408 DrawHighlightOnDC(hdc, TRUE,
2409 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
2412 for (i=0; i<2; i++) {
2413 if (premoveHighlightInfo.sq[i].x >= 0 &&
2414 premoveHighlightInfo.sq[i].y >= 0) {
2415 DrawHighlightOnDC(hdc, TRUE,
2416 premoveHighlightInfo.sq[i].x,
2417 premoveHighlightInfo.sq[i].y,
2423 /* Note: sqcolor is used only in monoMode */
2424 /* Note that this code is largely duplicated in woptions.c,
2425 function DrawSampleSquare, so that needs to be updated too */
2427 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
2432 if (appData.blindfold) return;
2434 if (appData.monoMode) {
2435 SelectObject(tmphdc, PieceBitmap(piece,
2436 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
2437 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
2438 sqcolor ? SRCCOPY : NOTSRCCOPY);
2441 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
2442 oldBrush = SelectObject(hdc, whitePieceBrush);
2443 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2445 /* Use black piece color for outline of white pieces */
2446 /* Not sure this looks really good (though xboard does it).
2447 Maybe better to have another selectable color, default black */
2448 SelectObject(hdc, blackPieceBrush); /* could have own brush */
2449 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2450 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2452 /* Use black for outline of white pieces */
2453 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2454 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);
2458 /* Use white piece color for details of black pieces */
2459 /* Requires filled-in solid bitmaps (BLACK_PIECE class); the
2460 WHITE_PIECE ones aren't always the right shape. */
2461 /* Not sure this looks really good (though xboard does it).
2462 Maybe better to have another selectable color, default medium gray? */
2463 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));
2464 oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */
2465 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2466 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2467 SelectObject(hdc, blackPieceBrush);
2468 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2470 /* Use square color for details of black pieces */
2471 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2472 oldBrush = SelectObject(hdc, blackPieceBrush);
2473 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2476 SelectObject(hdc, oldBrush);
2477 SelectObject(tmphdc, oldBitmap);
2482 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
2484 int row, column, x, y, square_color, piece_color;
2488 for (row = 0; row < BOARD_SIZE; row++) {
2489 for (column = 0; column < BOARD_SIZE; column++) {
2491 SquareToPos(row, column, &x, &y);
2493 piece = board[row][column];
2495 square_color = ((column + row) % 2) == 1;
2496 piece_color = (int) piece < (int) BlackPawn;
2498 if (appData.monoMode) {
2499 if (piece == EmptySquare) {
2500 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
2501 square_color ? WHITENESS : BLACKNESS);
2503 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
2506 oldBrush = SelectObject(hdc, square_color ?
2507 lightSquareBrush : darkSquareBrush);
2508 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
2509 SelectObject(hdc, oldBrush);
2510 if (piece != EmptySquare)
2511 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
2517 #define MAX_CLIPS 200 /* more than enough */
2520 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
2522 static Board lastReq, lastDrawn;
2523 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
2524 static int lastDrawnFlipView = 0;
2525 static int lastReqValid = 0, lastDrawnValid = 0;
2526 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
2529 HBITMAP bufferBitmap;
2532 HRGN clips[MAX_CLIPS];
2533 ChessSquare dragged_piece = EmptySquare;
2535 /* I'm undecided on this - this function figures out whether a full
2536 * repaint is necessary on its own, so there's no real reason to have the
2537 * caller tell it that. I think this can safely be set to FALSE - but
2538 * if we trust the callers not to request full repaints unnessesarily, then
2539 * we could skip some clipping work. In other words, only request a full
2540 * redraw when the majority of pieces have changed positions (ie. flip,
2541 * gamestart and similar) --Hawk
2543 Boolean fullrepaint = repaint;
2545 if (board == NULL) {
2546 if (!lastReqValid) {
2551 CopyBoard(lastReq, board);
2559 if (IsIconic(hwndMain)) {
2564 hdc = GetDC(hwndMain);
2565 if (!appData.monoMode) {
2566 SelectPalette(hdc, hPal, FALSE);
2567 RealizePalette(hdc);
2575 fprintf(debugFP, "*******************************\n"
2577 "dragInfo.from (%d,%d)\n"
2578 "dragInfo.start (%d,%d)\n"
2579 "dragInfo.pos (%d,%d)\n"
2580 "dragInfo.lastpos (%d,%d)\n",
2581 repaint ? "TRUE" : "FALSE",
2582 dragInfo.from.x, dragInfo.from.y,
2583 dragInfo.start.x, dragInfo.start.y,
2584 dragInfo.pos.x, dragInfo.pos.y,
2585 dragInfo.lastpos.x, dragInfo.lastpos.y);
2586 fprintf(debugFP, "prev: ");
2587 for (row = 0; row < 8; row++) {
2588 for (column = 0; column < 8; column++) {
2589 fprintf(debugFP, "%d ", lastDrawn[row][column]);
2592 fprintf(debugFP, "\n");
2593 fprintf(debugFP, "board: ");
2594 for (row = 0; row < 8; row++) {
2595 for (column = 0; column < 8; column++) {
2596 fprintf(debugFP, "%d ", board[row][column]);
2599 fprintf(debugFP, "\n");
2603 /* Create some work-DCs */
2604 hdcmem = CreateCompatibleDC(hdc);
2605 tmphdc = CreateCompatibleDC(hdc);
2607 /* Figure out which squares need updating by comparing the
2608 * newest board with the last drawn board and checking if
2609 * flipping has changed.
2611 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
2612 for (row = 0; row < 8; row++) {
2613 for (column = 0; column < 8; column++) {
2614 if (lastDrawn[row][column] != board[row][column]) {
2615 SquareToPos(row, column, &x, &y);
2616 clips[num_clips++] =
2617 CreateRectRgn(x, y, x + squareSize, y + squareSize);
2621 for (i=0; i<2; i++) {
2622 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
2623 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
2624 if (lastDrawnHighlight.sq[i].x >= 0 &&
2625 lastDrawnHighlight.sq[i].y >= 0) {
2626 SquareToPos(lastDrawnHighlight.sq[i].y,
2627 lastDrawnHighlight.sq[i].x, &x, &y);
2628 clips[num_clips++] =
2629 CreateRectRgn(x - lineGap, y - lineGap,
2630 x + squareSize + lineGap, y + squareSize + lineGap);
2632 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
2633 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
2634 clips[num_clips++] =
2635 CreateRectRgn(x - lineGap, y - lineGap,
2636 x + squareSize + lineGap, y + squareSize + lineGap);
2640 for (i=0; i<2; i++) {
2641 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
2642 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
2643 if (lastDrawnPremove.sq[i].x >= 0 &&
2644 lastDrawnPremove.sq[i].y >= 0) {
2645 SquareToPos(lastDrawnPremove.sq[i].y,
2646 lastDrawnPremove.sq[i].x, &x, &y);
2647 clips[num_clips++] =
2648 CreateRectRgn(x - lineGap, y - lineGap,
2649 x + squareSize + lineGap, y + squareSize + lineGap);
2651 if (premoveHighlightInfo.sq[i].x >= 0 &&
2652 premoveHighlightInfo.sq[i].y >= 0) {
2653 SquareToPos(premoveHighlightInfo.sq[i].y,
2654 premoveHighlightInfo.sq[i].x, &x, &y);
2655 clips[num_clips++] =
2656 CreateRectRgn(x - lineGap, y - lineGap,
2657 x + squareSize + lineGap, y + squareSize + lineGap);
2665 /* Create a buffer bitmap - this is the actual bitmap
2666 * being written to. When all the work is done, we can
2667 * copy it to the real DC (the screen). This avoids
2668 * the problems with flickering.
2670 GetClientRect(hwndMain, &Rect);
2671 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
2672 Rect.bottom-Rect.top+1);
2673 oldBitmap = SelectObject(hdcmem, bufferBitmap);
2674 if (!appData.monoMode) {
2675 SelectPalette(hdcmem, hPal, FALSE);
2678 /* Create clips for dragging */
2680 if (dragInfo.from.x >= 0) {
2681 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
2682 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2684 if (dragInfo.start.x >= 0) {
2685 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
2686 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2688 if (dragInfo.pos.x >= 0) {
2689 x = dragInfo.pos.x - squareSize / 2;
2690 y = dragInfo.pos.y - squareSize / 2;
2691 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2693 if (dragInfo.lastpos.x >= 0) {
2694 x = dragInfo.lastpos.x - squareSize / 2;
2695 y = dragInfo.lastpos.y - squareSize / 2;
2696 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2700 /* If dragging is in progress, we temporarely remove the piece */
2701 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
2702 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
2703 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
2706 /* Are we animating a move?
2708 * - remove the piece from the board (temporarely)
2709 * - calculate the clipping region
2712 if (animInfo.piece != EmptySquare) {
2713 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
2714 x = boardRect.left + animInfo.lastpos.x;
2715 y = boardRect.top + animInfo.lastpos.y;
2716 x2 = boardRect.left + animInfo.pos.x;
2717 y2 = boardRect.top + animInfo.pos.y;
2718 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
2719 /* Slight kludge. The real problem is that after AnimateMove is
2720 done, the position on the screen does not match lastDrawn.
2721 This currently causes trouble only on e.p. captures in
2722 atomic, where the piece moves to an empty square and then
2723 explodes. The old and new positions both had an empty square
2724 at the destination, but animation has drawn a piece there and
2725 we have to remember to erase it. */
2726 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
2730 /* No clips? Make sure we have fullrepaint set to TRUE */
2734 /* Set clipping on the memory DC */
2736 SelectClipRgn(hdcmem, clips[0]);
2737 for (x = 1; x < num_clips; x++) {
2738 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
2739 abort(); // this should never ever happen!
2743 /* Do all the drawing to the memory DC */
2744 DrawGridOnDC(hdcmem);
2745 DrawHighlightsOnDC(hdcmem);
2746 DrawBoardOnDC(hdcmem, board, tmphdc);
2747 DrawCoordsOnDC(hdcmem);
2749 /* Put the dragged piece back into place and draw it */
2750 if (dragged_piece != EmptySquare) {
2751 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
2752 x = dragInfo.pos.x - squareSize / 2;
2753 y = dragInfo.pos.y - squareSize / 2;
2754 DrawPieceOnDC(hdcmem, dragged_piece,
2755 ((int) dragged_piece < (int) BlackPawn),
2756 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
2759 /* Put the animated piece back into place and draw it */
2760 if (animInfo.piece != EmptySquare) {
2761 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
2762 x = boardRect.left + animInfo.pos.x;
2763 y = boardRect.top + animInfo.pos.y;
2764 DrawPieceOnDC(hdcmem, animInfo.piece,
2765 ((int) animInfo.piece < (int) BlackPawn),
2766 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
2769 /* Release the bufferBitmap by selecting in the old bitmap
2770 * and delete the memory DC
2772 SelectObject(hdcmem, oldBitmap);
2775 /* Set clipping on the target DC */
2777 SelectClipRgn(hdc, clips[0]);
2778 for (x = 1; x < num_clips; x++) {
2779 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
2780 abort(); // this should never ever happen!
2784 /* Copy the new bitmap onto the screen in one go.
2785 * This way we avoid any flickering
2787 oldBitmap = SelectObject(tmphdc, bufferBitmap);
2788 BitBlt(hdc, boardRect.left, boardRect.top,
2789 boardRect.right - boardRect.left,
2790 boardRect.bottom - boardRect.top,
2791 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
2792 SelectObject(tmphdc, oldBitmap);
2794 /* Massive cleanup */
2795 for (x = 0; x < num_clips; x++)
2796 DeleteObject(clips[x]);
2799 DeleteObject(bufferBitmap);
2802 ReleaseDC(hwndMain, hdc);
2804 if (lastDrawnFlipView != flipView) {
2806 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
2808 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
2811 CopyBoard(lastDrawn, board);
2812 lastDrawnHighlight = highlightInfo;
2813 lastDrawnPremove = premoveHighlightInfo;
2814 lastDrawnFlipView = flipView;
2819 /*---------------------------------------------------------------------------*\
2820 | CLIENT PAINT PROCEDURE
2821 | This is the main event-handler for the WM_PAINT message.
2823 \*---------------------------------------------------------------------------*/
2825 PaintProc(HWND hwnd)
2831 if(hdc = BeginPaint(hwnd, &ps)) {
2832 if (IsIconic(hwnd)) {
2833 DrawIcon(hdc, 2, 2, iconCurrent);
2835 if (!appData.monoMode) {
2836 SelectPalette(hdc, hPal, FALSE);
2837 RealizePalette(hdc);
2839 HDCDrawPosition(hdc, 1, NULL);
2841 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2842 ExtTextOut(hdc, messageRect.left, messageRect.top,
2843 ETO_CLIPPED|ETO_OPAQUE,
2844 &messageRect, messageText, strlen(messageText), NULL);
2845 SelectObject(hdc, oldFont);
2846 DisplayBothClocks();
2856 * If the user selects on a border boundary, return -1; if off the board,
2857 * return -2. Otherwise map the event coordinate to the square.
2858 * The offset boardRect.left or boardRect.top must already have been
2859 * subtracted from x.
2862 EventToSquare(int x)
2869 if ((x % (squareSize + lineGap)) >= squareSize)
2871 x /= (squareSize + lineGap);
2872 if (x >= BOARD_SIZE)
2883 DropEnable dropEnables[] = {
2884 { 'P', DP_Pawn, "Pawn" },
2885 { 'N', DP_Knight, "Knight" },
2886 { 'B', DP_Bishop, "Bishop" },
2887 { 'R', DP_Rook, "Rook" },
2888 { 'Q', DP_Queen, "Queen" },
2892 SetupDropMenu(HMENU hmenu)
2894 int i, count, enable;
2896 extern char white_holding[], black_holding[];
2899 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
2900 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2901 dropEnables[i].piece);
2903 while (p && *p++ == dropEnables[i].piece) count++;
2904 sprintf(item, "%s %d", dropEnables[i].name, count);
2905 enable = count > 0 || !appData.testLegality
2906 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2907 && !appData.icsActive);
2908 ModifyMenu(hmenu, dropEnables[i].command,
2909 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
2910 dropEnables[i].command, item);
2914 static int fromX = -1, fromY = -1, toX, toY;
2916 /* Event handler for mouse messages */
2918 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2922 static int recursive = 0;
2924 BOOLEAN saveAnimate;
2925 static BOOLEAN sameAgain = FALSE;
2928 if (message == WM_MBUTTONUP) {
2929 /* Hideous kludge to fool TrackPopupMenu into paying attention
2930 to the middle button: we simulate pressing the left button too!
2932 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
2933 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
2939 pt.x = LOWORD(lParam);
2940 pt.y = HIWORD(lParam);
2941 x = EventToSquare(pt.x - boardRect.left);
2942 y = EventToSquare(pt.y - boardRect.top);
2943 if (!flipView && y >= 0) {
2944 y = BOARD_SIZE - 1 - y;
2946 if (flipView && x >= 0) {
2947 x = BOARD_SIZE - 1 - x;
2951 case WM_LBUTTONDOWN:
2955 /* Downclick vertically off board; check if on clock */
2956 if (PtInRect((LPRECT) &whiteRect, pt)) {
2957 if (gameMode == EditPosition) {
2958 SetWhiteToPlayEvent();
2959 } else if (gameMode == IcsPlayingBlack ||
2960 gameMode == MachinePlaysWhite) {
2963 } else if (PtInRect((LPRECT) &blackRect, pt)) {
2964 if (gameMode == EditPosition) {
2965 SetBlackToPlayEvent();
2966 } else if (gameMode == IcsPlayingWhite ||
2967 gameMode == MachinePlaysBlack) {
2971 if (!appData.highlightLastMove) {
2973 DrawPosition(FALSE, NULL);
2976 dragInfo.start.x = dragInfo.start.y = -1;
2977 dragInfo.from = dragInfo.start;
2979 } else if (x < 0 || y < 0) {
2981 } else if (fromX == x && fromY == y) {
2982 /* Downclick on same square again */
2984 DrawPosition(FALSE, NULL);
2986 } else if (fromX != -1) {
2987 /* Downclick on different square */
2988 ChessSquare pdown, pup;
2989 pdown = boards[currentMove][fromY][fromX];
2990 pup = boards[currentMove][y][x];
2991 if (gameMode == EditPosition ||
2992 !((WhitePawn <= pdown && pdown <= WhiteKing &&
2993 WhitePawn <= pup && pup <= WhiteKing) ||
2994 (BlackPawn <= pdown && pdown <= BlackKing &&
2995 BlackPawn <= pup && pup <= BlackKing))) {
2996 /* EditPosition, empty square, or different color piece;
2997 click-click move is possible */
3000 if (IsPromotion(fromX, fromY, toX, toY)) {
3001 if (appData.alwaysPromoteToQueen) {
3002 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3003 if (!appData.highlightLastMove) {
3005 DrawPosition(FALSE, NULL);
3008 SetHighlights(fromX, fromY, toX, toY);
3009 DrawPosition(FALSE, NULL);
3010 PromotionPopup(hwnd);
3012 } else { /* not a promotion */
3013 if (appData.animate || appData.highlightLastMove) {
3014 SetHighlights(fromX, fromY, toX, toY);
3018 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3019 if (appData.animate && !appData.highlightLastMove) {
3021 DrawPosition(FALSE, NULL);
3024 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3029 DrawPosition(FALSE, NULL);
3031 /* First downclick, or restart on a square with same color piece */
3032 if (!frozen && OKToStartUserMove(x, y)) {
3035 dragInfo.lastpos = pt;
3036 dragInfo.from.x = fromX;
3037 dragInfo.from.y = fromY;
3038 dragInfo.start = dragInfo.from;
3039 SetCapture(hwndMain);
3042 dragInfo.start.x = dragInfo.start.y = -1;
3043 dragInfo.from = dragInfo.start;
3049 if (fromX == -1) break;
3050 if (x == fromX && y == fromY) {
3051 dragInfo.from.x = dragInfo.from.y = -1;
3052 /* Upclick on same square */
3054 /* Clicked same square twice: abort click-click move */
3057 ClearPremoveHighlights();
3059 /* First square clicked: start click-click move */
3060 SetHighlights(fromX, fromY, -1, -1);
3062 DrawPosition(FALSE, NULL);
3063 } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {
3064 /* Errant click; ignore */
3067 /* Finish drag move */
3068 dragInfo.from.x = dragInfo.from.y = -1;
3071 saveAnimate = appData.animate; /* sorry, Hawk :) */
3072 appData.animate = appData.animate && !appData.animateDragging;
3073 if (IsPromotion(fromX, fromY, toX, toY)) {
3074 if (appData.alwaysPromoteToQueen) {
3075 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3077 DrawPosition(FALSE, NULL);
3078 PromotionPopup(hwnd);
3081 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3083 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3084 appData.animate = saveAnimate;
3086 if (appData.highlightDragging && !appData.highlightLastMove) {
3089 if (appData.animate || appData.animateDragging ||
3090 appData.highlightDragging || gotPremove) {
3091 DrawPosition(FALSE, NULL);
3094 dragInfo.start.x = dragInfo.start.y = -1;
3095 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
3099 if ((appData.animateDragging || appData.highlightDragging)
3100 && (wParam & MK_LBUTTON)
3101 && dragInfo.from.x >= 0) {
3102 if (appData.animateDragging) {
3105 if (appData.highlightDragging) {
3106 SetHighlights(fromX, fromY, x, y);
3108 DrawPosition(FALSE, NULL);
3109 dragInfo.lastpos = dragInfo.pos;
3113 /* Mouse Wheel is being rolled forward
3114 * Play moves forward
3116 if ((short)HIWORD(wParam) > 0)
3117 if (forwardMostMove > 0 && currentMove != forwardMostMove)
3119 /* Mouse Wheel is being rolled backward
3120 * Play moves backward
3122 if ((short)HIWORD(wParam) < 0)
3123 if (currentMove > 0) BackwardEvent();
3125 case WM_MBUTTONDOWN:
3126 case WM_RBUTTONDOWN:
3130 dragInfo.pos.x = dragInfo.pos.y = -1;
3131 dragInfo.start.x = dragInfo.start.y = -1;
3132 dragInfo.from = dragInfo.start;
3133 dragInfo.lastpos = dragInfo.pos;
3134 if (appData.highlightDragging) {
3137 DrawPosition(TRUE, NULL);
3142 if (x < 0 || y < 0) break;
3145 if (message == WM_MBUTTONDOWN) {
3146 buttonCount = 3; /* even if system didn't think so */
3147 if (wParam & MK_SHIFT)
3148 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3150 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3151 } else { /* message == WM_RBUTTONDOWN */
3153 if (buttonCount == 3) {
3154 if (wParam & MK_SHIFT)
3155 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3157 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3159 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3162 /* Just have one menu, on the right button. Windows users don't
3163 think to try the middle one, and sometimes other software steals
3164 it, or it doesn't really exist. */
3165 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3169 case IcsPlayingWhite:
3170 case IcsPlayingBlack:
3172 case MachinePlaysWhite:
3173 case MachinePlaysBlack:
3174 if (appData.testLegality &&
3175 gameInfo.variant != VariantBughouse &&
3176 gameInfo.variant != VariantCrazyhouse) break;
3177 if (x < 0 || y < 0) break;
3180 hmenu = LoadMenu(hInst, "DropPieceMenu");
3181 SetupDropMenu(hmenu);
3182 MenuPopup(hwnd, pt, hmenu, -1);
3193 /* Preprocess messages for buttons in main window */
3195 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3197 int id = GetWindowLong(hwnd, GWL_ID);
3200 for (i=0; i<N_BUTTONS; i++) {
3201 if (buttonDesc[i].id == id) break;
3203 if (i == N_BUTTONS) return 0;
3209 dir = (wParam == VK_LEFT) ? -1 : 1;
3210 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
3217 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
3220 if (appData.icsActive) {
3221 if (GetKeyState(VK_SHIFT) < 0) {
3223 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3224 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3228 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3229 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3236 if (appData.icsActive) {
3237 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3238 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3240 SendMessage(h, WM_CHAR, wParam, lParam);
3242 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
3243 PopUpMoveDialog((char)wParam);
3249 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
3252 /* Process messages for Promotion dialog box */
3254 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
3259 case WM_INITDIALOG: /* message: initialize dialog box */
3260 /* Center the dialog over the application window */
3261 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
3262 ShowWindow(GetDlgItem(hDlg, PB_King),
3263 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3264 gameInfo.variant == VariantGiveaway) ?
3268 case WM_COMMAND: /* message: received a command */
3269 switch (LOWORD(wParam)) {
3271 EndDialog(hDlg, TRUE); /* Exit the dialog */
3273 DrawPosition(FALSE, NULL);
3293 EndDialog(hDlg, TRUE); /* Exit the dialog */
3294 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3295 if (!appData.highlightLastMove) {
3297 DrawPosition(FALSE, NULL);
3304 /* Pop up promotion dialog */
3306 PromotionPopup(HWND hwnd)
3310 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
3311 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
3312 hwnd, (DLGPROC)lpProc);
3313 FreeProcInstance(lpProc);
3316 /* Toggle ShowThinking */
3318 ToggleShowThinking()
3320 ShowThinkingEvent(!appData.showThinking);
3324 LoadGameDialog(HWND hwnd, char* title)
3328 char fileTitle[MSG_SIZ];
3329 f = OpenFileDialog(hwnd, FALSE, "",
3330 appData.oldSaveStyle ? "gam" : "pgn",
3332 title, &number, fileTitle, NULL);
3334 cmailMsgLoaded = FALSE;
3336 int error = GameListBuild(f);
3338 DisplayError("Cannot build game list", error);
3339 } else if (!ListEmpty(&gameList) &&
3340 ((ListGame *) gameList.tailPred)->number > 1) {
3341 GameListPopUp(f, fileTitle);
3347 LoadGame(f, number, fileTitle, FALSE);
3352 ChangedConsoleFont()
3355 CHARRANGE tmpsel, sel;
3356 MyFont *f = font[boardSize][CONSOLE_FONT];
3357 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
3358 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3361 cfmt.cbSize = sizeof(CHARFORMAT);
3362 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
3363 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
3364 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
3365 * size. This was undocumented in the version of MSVC++ that I had
3366 * when I wrote the code, but is apparently documented now.
3368 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
3369 cfmt.bCharSet = f->lf.lfCharSet;
3370 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
3371 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3372 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3373 /* Why are the following seemingly needed too? */
3374 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3375 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3376 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
3378 tmpsel.cpMax = -1; /*999999?*/
3379 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
3380 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
3381 /* Trying putting this here too. It still seems to tickle a RichEdit
3382 * bug: sometimes RichEdit indents the first line of a paragraph too.
3384 paraf.cbSize = sizeof(paraf);
3385 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
3386 paraf.dxStartIndent = 0;
3387 paraf.dxOffset = WRAP_INDENT;
3388 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
3389 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
3392 /*---------------------------------------------------------------------------*\
3394 * Window Proc for main window
3396 \*---------------------------------------------------------------------------*/
3398 /* Process messages for main window, etc. */
3400 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3407 char fileTitle[MSG_SIZ];
3412 case WM_PAINT: /* message: repaint portion of window */
3417 if (IsIconic(hwnd)) {
3418 /* Cheat; change the message */
3419 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
3421 return (DefWindowProc(hwnd, message, wParam, lParam));
3425 case WM_LBUTTONDOWN:
3426 case WM_MBUTTONDOWN:
3427 case WM_RBUTTONDOWN:
3433 MouseEvent(hwnd, message, wParam, lParam);
3438 if (appData.icsActive) {
3439 if (wParam == '\t') {
3440 if (GetKeyState(VK_SHIFT) < 0) {
3442 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3443 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3447 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3448 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3452 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3453 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3455 SendMessage(h, message, wParam, lParam);
3457 } else if (isalpha((char)wParam) || isdigit((char)wParam)) {
3458 PopUpMoveDialog((char)wParam);
3462 case WM_PALETTECHANGED:
3463 if (hwnd != (HWND)wParam && !appData.monoMode) {
3465 HDC hdc = GetDC(hwndMain);
3466 SelectPalette(hdc, hPal, TRUE);
3467 nnew = RealizePalette(hdc);
3469 paletteChanged = TRUE;
3473 InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/
3476 ReleaseDC(hwnd, hdc);
3480 case WM_QUERYNEWPALETTE:
3481 if (!appData.monoMode /*&& paletteChanged*/) {
3483 HDC hdc = GetDC(hwndMain);
3484 paletteChanged = FALSE;
3485 SelectPalette(hdc, hPal, FALSE);
3486 nnew = RealizePalette(hdc);
3488 InvalidateRect(hwnd, &boardRect, FALSE);
3490 ReleaseDC(hwnd, hdc);
3495 case WM_COMMAND: /* message: command from application menu */
3496 wmId = LOWORD(wParam);
3497 wmEvent = HIWORD(wParam);
3506 LoadGameDialog(hwnd, "Load Game from File");
3509 case IDM_LoadNextGame:
3513 case IDM_LoadPrevGame:
3517 case IDM_ReloadGame:
3521 case IDM_LoadPosition:
3522 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
3526 f = OpenFileDialog(hwnd, FALSE, "",
3527 appData.oldSaveStyle ? "pos" : "fen",
3529 "Load Position from File", &number, fileTitle, NULL);
3531 LoadPosition(f, number, fileTitle);
3535 case IDM_LoadNextPosition:
3539 case IDM_LoadPrevPosition:
3543 case IDM_ReloadPosition:
3548 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
3549 f = OpenFileDialog(hwnd, TRUE, defName,
3550 appData.oldSaveStyle ? "gam" : "pgn",
3552 "Save Game to File", NULL, fileTitle, NULL);
3558 case IDM_SavePosition:
3559 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
3560 f = OpenFileDialog(hwnd, TRUE, defName,
3561 appData.oldSaveStyle ? "pos" : "fen",
3563 "Save Position to File", NULL, fileTitle, NULL);
3565 SavePosition(f, 0, "");
3570 CopyGameToClipboard();
3574 PasteGameFromClipboard();
3577 case IDM_CopyPosition:
3578 CopyFENToClipboard();
3581 case IDM_PastePosition:
3582 PasteFENFromClipboard();
3589 case IDM_ReloadCMailMsg:
3591 ReloadCmailMsgEvent(FALSE);
3595 ShowWindow(hwnd, SW_MINIMIZE);
3602 case IDM_MachineWhite:
3603 MachineWhiteEvent();
3605 * refresh the tags dialog only if it's visible
3607 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
3609 tags = PGNTags(&gameInfo);
3610 TagsPopUp(tags, CmailMsg());
3615 case IDM_MachineBlack:
3616 MachineBlackEvent();
3618 * refresh the tags dialog only if it's visible
3620 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
3622 tags = PGNTags(&gameInfo);
3623 TagsPopUp(tags, CmailMsg());
3628 case IDM_TwoMachines:
3631 * refresh the tags dialog only if it's visible
3633 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
3635 tags = PGNTags(&gameInfo);
3636 TagsPopUp(tags, CmailMsg());
3641 case IDM_AnalysisMode:
3642 if (!first.analysisSupport) {
3643 sprintf(buf, "%s does not support analysis", first.tidy);
3644 DisplayError(buf, 0);
3646 /* icsEngineAnlyze */
3647 if (appData.icsActive) {
3648 if (gameMode != IcsObserving) {
3649 sprintf(buf, "You are not observing a game");
3650 DisplayError(buf, 0);
3652 if (appData.icsEngineAnalyze) {
3653 if (appData.debugMode)
3654 fprintf(debugFP, "Found unexpected active ICS engine analyze \n");
3661 /* if enable, user want disable icsEngineAnalyze */
3662 if (appData.icsEngineAnalyze) {
3667 appData.icsEngineAnalyze = TRUE;
3668 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
3671 if (!appData.showThinking) ToggleShowThinking();
3675 case IDM_AnalyzeFile:
3676 if (!first.analysisSupport) {
3678 sprintf(buf, "%s does not support analysis", first.tidy);
3679 DisplayError(buf, 0);
3681 if (!appData.showThinking) ToggleShowThinking();
3683 LoadGameDialog(hwnd, "Analyze Game from File");
3684 AnalysisPeriodicEvent(1);
3696 case IDM_EditPosition:
3697 EditPositionEvent();
3704 case IDM_ShowGameList:
3712 case IDM_EditComment:
3713 if (commentDialogUp && editComment) {
3756 case IDM_StopObserving:
3757 StopObservingEvent();
3760 case IDM_StopExamining:
3761 StopExaminingEvent();
3764 case IDM_TypeInMove:
3765 PopUpMoveDialog('\000');
3792 case IDM_TruncateGame:
3793 TruncateGameEvent();
3800 case IDM_RetractMove:
3805 flipView = !flipView;
3806 DrawPosition(FALSE, NULL);
3809 case IDM_GeneralOptions:
3810 GeneralOptionsPopup(hwnd);
3813 case IDM_BoardOptions:
3814 BoardOptionsPopup(hwnd);
3817 case IDM_IcsOptions:
3818 IcsOptionsPopup(hwnd);
3822 FontsOptionsPopup(hwnd);
3826 SoundOptionsPopup(hwnd);
3830 CommPortOptionsPopup(hwnd);
3833 case IDM_LoadOptions:
3834 LoadOptionsPopup(hwnd);
3837 case IDM_SaveOptions:
3838 SaveOptionsPopup(hwnd);
3841 case IDM_TimeControl:
3842 TimeControlOptionsPopup(hwnd);
3845 case IDM_SaveSettings:
3846 SaveSettings(settingsFileName);
3849 case IDM_SaveSettingsOnExit:
3850 saveSettingsOnExit = !saveSettingsOnExit;
3851 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
3852 MF_BYCOMMAND|(saveSettingsOnExit ?
3853 MF_CHECKED : MF_UNCHECKED));
3869 appData.debugMode = !appData.debugMode;
3870 if (appData.debugMode) {
3872 GetCurrentDirectory(MSG_SIZ, dir);
3873 SetCurrentDirectory(installDir);
3874 debugFP = fopen("WinBoard.debug", "w");
3875 SetCurrentDirectory(dir);
3876 setbuf(debugFP, NULL);
3883 case IDM_HELPCONTENTS:
3884 if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
3885 MessageBox (GetFocus(),
3886 "Unable to activate help",
3887 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3891 case IDM_HELPSEARCH:
3892 if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
3893 MessageBox (GetFocus(),
3894 "Unable to activate help",
3895 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3900 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
3901 MessageBox (GetFocus(),
3902 "Unable to activate help",
3903 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3908 lpProc = MakeProcInstance((FARPROC)About, hInst);
3910 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
3911 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
3912 FreeProcInstance(lpProc);
3915 case IDM_DirectCommand1:
3916 AskQuestionEvent("Direct Command",
3917 "Send to chess program:", "", "1");
3919 case IDM_DirectCommand2:
3920 AskQuestionEvent("Direct Command",
3921 "Send to second chess program:", "", "2");
3925 EditPositionMenuEvent(WhitePawn, fromX, fromY);
3929 case EP_WhiteKnight:
3930 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
3934 case EP_WhiteBishop:
3935 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
3940 EditPositionMenuEvent(WhiteRook, fromX, fromY);
3945 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
3950 EditPositionMenuEvent(WhiteKing, fromX, fromY);
3955 EditPositionMenuEvent(BlackPawn, fromX, fromY);
3959 case EP_BlackKnight:
3960 EditPositionMenuEvent(BlackKnight, fromX, fromY);
3964 case EP_BlackBishop:
3965 EditPositionMenuEvent(BlackBishop, fromX, fromY);
3970 EditPositionMenuEvent(BlackRook, fromX, fromY);
3975 EditPositionMenuEvent(BlackQueen, fromX, fromY);
3980 EditPositionMenuEvent(BlackKing, fromX, fromY);
3984 case EP_EmptySquare:
3985 EditPositionMenuEvent(EmptySquare, fromX, fromY);
3990 EditPositionMenuEvent(ClearBoard, fromX, fromY);
3995 EditPositionMenuEvent(WhitePlay, fromX, fromY);
4000 EditPositionMenuEvent(BlackPlay, fromX, fromY);
4005 DropMenuEvent(WhitePawn, fromX, fromY);
4010 DropMenuEvent(WhiteKnight, fromX, fromY);
4015 DropMenuEvent(WhiteBishop, fromX, fromY);
4020 DropMenuEvent(WhiteRook, fromX, fromY);
4025 DropMenuEvent(WhiteQueen, fromX, fromY);
4030 return (DefWindowProc(hwnd, message, wParam, lParam));
4036 case CLOCK_TIMER_ID:
4037 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
4038 clockTimerEvent = 0;
4039 DecrementClocks(); /* call into back end */
4041 case LOAD_GAME_TIMER_ID:
4042 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
4043 loadGameTimerEvent = 0;
4044 AutoPlayGameLoop(); /* call into back end */
4046 case ANALYSIS_TIMER_ID:
4047 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
4048 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
4049 AnalysisPeriodicEvent(0);
4051 KillTimer(hwnd, analysisTimerEvent);
4052 analysisTimerEvent = 0;
4055 case DELAYED_TIMER_ID:
4056 KillTimer(hwnd, delayedTimerEvent);
4057 delayedTimerEvent = 0;
4058 delayedTimerCallback();
4064 InputEvent(hwnd, message, wParam, lParam);
4067 case WM_ENTERSIZEMOVE:
4068 if (hwnd == hwndMain) {
4075 if (hwnd == hwndMain) {
4076 lastSizing = wParam;
4080 case WM_EXITSIZEMOVE:
4081 if (hwnd == hwndMain) {
4083 doingSizing = FALSE;
4084 InvalidateRect(hwnd, &boardRect, FALSE);
4085 GetClientRect(hwnd, &client);
4086 ResizeBoard(client.right, client.bottom, lastSizing);
4091 case WM_DESTROY: /* message: window being destroyed */
4096 if (hwnd == hwndMain) {
4101 default: /* Passes it on if unprocessed */
4102 return (DefWindowProc(hwnd, message, wParam, lParam));
4107 /*---------------------------------------------------------------------------*\
4109 * Misc utility routines
4111 \*---------------------------------------------------------------------------*/
4114 * Decent random number generator, at least not as bad as Windows
4115 * standard rand, which returns a value in the range 0 to 0x7fff.
4117 unsigned int randstate;
4122 randstate = randstate * 1664525 + 1013904223;
4123 return (int) randstate & 0x7fffffff;
4127 mysrandom(unsigned int seed)
4134 * returns TRUE if user selects a different color, FALSE otherwise
4138 ChangeColor(HWND hwnd, COLORREF *which)
4140 static BOOL firstTime = TRUE;
4141 static DWORD customColors[16];
4148 /* Make initial colors in use available as custom colors */
4149 /* Should we put the compiled-in defaults here instead? */
4151 customColors[i++] = lightSquareColor & 0xffffff;
4152 customColors[i++] = darkSquareColor & 0xffffff;
4153 customColors[i++] = whitePieceColor & 0xffffff;
4154 customColors[i++] = blackPieceColor & 0xffffff;
4155 customColors[i++] = highlightSquareColor & 0xffffff;
4156 customColors[i++] = premoveHighlightColor & 0xffffff;
4158 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
4159 customColors[i++] = textAttribs[ccl].color;
4161 while (i < 16) customColors[i++] = RGB(255, 255, 255);
4165 cc.lStructSize = sizeof(cc);
4166 cc.hwndOwner = hwnd;
4167 cc.hInstance = NULL;
4168 cc.rgbResult = (DWORD) (*which & 0xffffff);
4169 cc.lpCustColors = (LPDWORD) customColors;
4170 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
4172 if (!ChooseColor(&cc)) return FALSE;
4174 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
4175 if (newcolor == *which) return FALSE;
4180 InitDrawingColors();
4181 InvalidateRect(hwnd, &boardRect, FALSE);
4186 MyLoadSound(MySound *ms)
4192 if (ms->data) free(ms->data);
4195 switch (ms->name[0]) {
4201 /* System sound from Control Panel. Don't preload here. */
4205 if (ms->name[1] == NULLCHAR) {
4206 /* "!" alone = silence */
4209 /* Builtin wave resource. Error if not found. */
4210 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
4211 if (h == NULL) break;
4212 ms->data = (void *)LoadResource(hInst, h);
4213 if (h == NULL) break;
4218 /* .wav file. Error if not found. */
4219 f = fopen(ms->name, "rb");
4220 if (f == NULL) break;
4221 if (fstat(fileno(f), &st) < 0) break;
4222 ms->data = malloc(st.st_size);
4223 if (fread(ms->data, st.st_size, 1, f) < 1) break;
4230 sprintf(buf, "Error loading sound %s", ms->name);
4231 DisplayError(buf, GetLastError());
4237 MyPlaySound(MySound *ms)
4240 switch (ms->name[0]) {
4246 /* System sound from Control Panel (deprecated feature).
4247 "$" alone or an unset sound name gets default beep (still in use). */
4249 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
4251 if (!ok) ok = MessageBeep(MB_OK);
4254 /* Builtin wave resource, or "!" alone for silence */
4256 if (ms->data == NULL) return FALSE;
4257 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4263 /* .wav file. Error if not found. */
4264 if (ms->data == NULL) return FALSE;
4265 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4268 /* Don't print an error: this can happen innocently if the sound driver
4269 is busy; for instance, if another instance of WinBoard is playing
4270 a sound at about the same time. */
4274 sprintf(buf, "Error playing sound %s", ms->name);
4275 DisplayError(buf, GetLastError());
4283 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4287 static UINT *number; /* gross that this is static */
4290 case WM_INITDIALOG: /* message: initialize dialog box */
4291 /* Center the dialog over the application window */
4292 ofn = (OPENFILENAME *) lParam;
4293 if (ofn->Flags & OFN_ENABLETEMPLATE) {
4294 number = (UINT *) ofn->lCustData;
4295 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
4299 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4300 return FALSE; /* Allow for further processing */
4303 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
4304 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
4306 return FALSE; /* Allow for further processing */
4312 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
4314 static UINT *number;
4315 OPENFILENAME *ofname;
4319 ofname = (OPENFILENAME *)lParam;
4320 number = (UINT *)(ofname->lCustData);
4323 ofnot = (OFNOTIFY *)lParam;
4324 if (ofnot->hdr.code == CDN_FILEOK) {
4325 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
4334 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,
4335 char *nameFilt, char *dlgTitle, UINT *number,
4336 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
4338 OPENFILENAME openFileName;
4342 if (fileName == NULL) fileName = buf1;
4343 if (defName == NULL) {
4344 strcpy(fileName, "*.");
4345 strcat(fileName, defExt);
4347 strcpy(fileName, defName);
4349 if (fileTitle) strcpy(fileTitle, "");
4350 if (number) *number = 0;
4352 openFileName.lStructSize = sizeof(OPENFILENAME);
4353 openFileName.hwndOwner = hwnd;
4354 openFileName.hInstance = (HANDLE) hInst;
4355 openFileName.lpstrFilter = nameFilt;
4356 openFileName.lpstrCustomFilter = (LPSTR) NULL;
4357 openFileName.nMaxCustFilter = 0L;
4358 openFileName.nFilterIndex = 1L;
4359 openFileName.lpstrFile = fileName;
4360 openFileName.nMaxFile = MSG_SIZ;
4361 openFileName.lpstrFileTitle = fileTitle;
4362 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
4363 openFileName.lpstrInitialDir = NULL;
4364 openFileName.lpstrTitle = dlgTitle;
4365 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
4366 | (write ? 0 : OFN_FILEMUSTEXIST)
4367 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
4368 | (oldDialog ? 0 : OFN_EXPLORER);
4369 openFileName.nFileOffset = 0;
4370 openFileName.nFileExtension = 0;
4371 openFileName.lpstrDefExt = defExt;
4372 openFileName.lCustData = (LONG) number;
4373 openFileName.lpfnHook = oldDialog ?
4374 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
4375 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
4377 if (write ? GetSaveFileName(&openFileName) :
4378 GetOpenFileName(&openFileName)) {
4380 f = fopen(openFileName.lpstrFile, write ? "a" : "rb");
4382 MessageBox(hwnd, "File open failed", NULL,
4383 MB_OK|MB_ICONEXCLAMATION);
4387 int err = CommDlgExtendedError();
4388 if (err != 0) DisplayError("Internal error in file dialog box", err);
4397 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
4399 HMENU hmenuTrackPopup; /* floating pop-up menu */
4402 * Get the first pop-up menu in the menu template. This is the
4403 * menu that TrackPopupMenu displays.
4405 hmenuTrackPopup = GetSubMenu(hmenu, 0);
4407 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
4410 * TrackPopup uses screen coordinates, so convert the
4411 * coordinates of the mouse click to screen coordinates.
4413 ClientToScreen(hwnd, (LPPOINT) &pt);
4415 /* Draw and track the floating pop-up menu. */
4416 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
4417 pt.x, pt.y, 0, hwnd, NULL);
4419 /* Destroy the menu.*/
4425 int sizeX, sizeY, newSizeX, newSizeY;
4427 } ResizeEditPlusButtonsClosure;
4430 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
4432 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
4436 if (hChild == cl->hText) return TRUE;
4437 GetWindowRect(hChild, &rect); /* gives screen coords */
4438 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
4439 pt.y = rect.top + cl->newSizeY - cl->sizeY;
4440 ScreenToClient(cl->hDlg, &pt);
4441 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
4442 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
4446 /* Resize a dialog that has a (rich) edit field filling most of
4447 the top, with a row of buttons below */
4449 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
4452 int newTextHeight, newTextWidth;
4453 ResizeEditPlusButtonsClosure cl;
4455 /*if (IsIconic(hDlg)) return;*/
4456 if (newSizeX == sizeX && newSizeY == sizeY) return;
4458 cl.hdwp = BeginDeferWindowPos(8);
4460 GetWindowRect(hText, &rectText); /* gives screen coords */
4461 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
4462 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
4463 if (newTextHeight < 0) {
4464 newSizeY += -newTextHeight;
4467 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
4468 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
4474 cl.newSizeX = newSizeX;
4475 cl.newSizeY = newSizeY;
4476 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
4478 EndDeferWindowPos(cl.hdwp);
4481 /* Center one window over another */
4482 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
4484 RECT rChild, rParent;
4485 int wChild, hChild, wParent, hParent;
4486 int wScreen, hScreen, xNew, yNew;
4489 /* Get the Height and Width of the child window */
4490 GetWindowRect (hwndChild, &rChild);
4491 wChild = rChild.right - rChild.left;
4492 hChild = rChild.bottom - rChild.top;
4494 /* Get the Height and Width of the parent window */
4495 GetWindowRect (hwndParent, &rParent);
4496 wParent = rParent.right - rParent.left;
4497 hParent = rParent.bottom - rParent.top;
4499 /* Get the display limits */
4500 hdc = GetDC (hwndChild);
4501 wScreen = GetDeviceCaps (hdc, HORZRES);
4502 hScreen = GetDeviceCaps (hdc, VERTRES);
4503 ReleaseDC(hwndChild, hdc);
4505 /* Calculate new X position, then adjust for screen */
4506 xNew = rParent.left + ((wParent - wChild) /2);
4509 } else if ((xNew+wChild) > wScreen) {
4510 xNew = wScreen - wChild;
4513 /* Calculate new Y position, then adjust for screen */
4514 yNew = rParent.top + ((hParent - hChild) /2);
4517 } else if ((yNew+hChild) > hScreen) {
4518 yNew = hScreen - hChild;
4521 /* Set it, and return */
4522 return SetWindowPos (hwndChild, NULL,
4523 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4526 /*---------------------------------------------------------------------------*\
4528 * Startup Dialog functions
4530 \*---------------------------------------------------------------------------*/
4532 InitComboStrings(HANDLE hwndCombo, char **cd)
4534 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4536 while (*cd != NULL) {
4537 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
4543 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
4548 if (str[0] == '@') {
4549 FILE* f = fopen(str + 1, "r");
4551 DisplayFatalError(str + 1, errno, 2);
4554 len = fread(buf1, 1, sizeof(buf1)-1, f);
4556 buf1[len] = NULLCHAR;
4560 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4564 char *end = strchr(str, '\n');
4565 if (end == NULL) return;
4566 memcpy(buf, str, end - str);
4567 buf[end - str] = NULLCHAR;
4568 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
4574 SetStartupDialogEnables(HWND hDlg)
4576 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
4577 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4578 appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));
4579 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
4580 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
4581 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
4582 IsDlgButtonChecked(hDlg, OPT_ChessServer));
4583 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
4584 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
4585 EnableWindow(GetDlgItem(hDlg, IDOK),
4586 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4587 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
4588 IsDlgButtonChecked(hDlg, OPT_View));
4592 QuoteForFilename(char *filename)
4595 dquote = strchr(filename, '"') != NULL;
4596 space = strchr(filename, ' ') != NULL;
4597 if (dquote || space) {
4609 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
4614 InitComboStringsFromOption(hwndCombo, nthnames);
4615 q = QuoteForFilename(nthcp);
4616 sprintf(buf, "%s%s%s", q, nthcp, q);
4617 if (*nthdir != NULLCHAR) {
4618 q = QuoteForFilename(nthdir);
4619 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
4621 if (*nthcp == NULLCHAR) {
4622 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4623 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4624 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4625 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4630 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4638 /* Center the dialog */
4639 CenterWindow (hDlg, GetDesktopWindow());
4640 /* Initialize the dialog items */
4641 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
4642 appData.firstChessProgram, "fd", appData.firstDirectory,
4643 firstChessProgramNames);
4644 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
4645 appData.secondChessProgram, "sd", appData.secondDirectory,
4646 secondChessProgramNames);
4647 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
4648 InitComboStringsFromOption(hwndCombo, icsNames);
4649 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
4650 if (*appData.icsHelper != NULLCHAR) {
4651 char *q = QuoteForFilename(appData.icsHelper);
4652 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
4654 if (*appData.icsHost == NULLCHAR) {
4655 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4656 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
4657 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4658 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4659 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4662 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
4663 } else if (appData.icsActive) {
4664 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
4665 } else if (appData.noChessProgram) {
4666 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
4668 SetStartupDialogEnables(hDlg);
4672 switch (LOWORD(wParam)) {
4674 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
4675 strcpy(buf, "/fcp=");
4676 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4678 ParseArgs(StringGet, &p);
4679 strcpy(buf, "/scp=");
4680 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4682 ParseArgs(StringGet, &p);
4683 appData.noChessProgram = FALSE;
4684 appData.icsActive = FALSE;
4685 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
4686 strcpy(buf, "/ics /icshost=");
4687 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4689 ParseArgs(StringGet, &p);
4690 if (appData.zippyPlay) {
4691 strcpy(buf, "/fcp=");
4692 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4694 ParseArgs(StringGet, &p);
4696 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
4697 appData.noChessProgram = TRUE;
4698 appData.icsActive = FALSE;
4700 MessageBox(hDlg, "Choose an option, or cancel to exit",
4701 "Option Error", MB_OK|MB_ICONEXCLAMATION);
4704 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
4705 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
4707 ParseArgs(StringGet, &p);
4709 EndDialog(hDlg, TRUE);
4716 case IDM_HELPCONTENTS:
4717 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
4718 MessageBox (GetFocus(),
4719 "Unable to activate help",
4720 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
4725 SetStartupDialogEnables(hDlg);
4733 /*---------------------------------------------------------------------------*\
4735 * About box dialog functions
4737 \*---------------------------------------------------------------------------*/
4739 /* Process messages for "About" dialog box */
4741 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4744 case WM_INITDIALOG: /* message: initialize dialog box */
4745 /* Center the dialog over the application window */
4746 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4747 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
4750 case WM_COMMAND: /* message: received a command */
4751 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
4752 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
4753 EndDialog(hDlg, TRUE); /* Exit the dialog */
4761 /*---------------------------------------------------------------------------*\
4763 * Comment Dialog functions
4765 \*---------------------------------------------------------------------------*/
4768 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4770 static HANDLE hwndText = NULL;
4771 int len, newSizeX, newSizeY, flags;
4772 static int sizeX, sizeY;
4778 case WM_INITDIALOG: /* message: initialize dialog box */
4779 /* Initialize the dialog items */
4780 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4781 SetDlgItemText(hDlg, OPT_CommentText, commentText);
4782 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
4783 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
4784 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
4785 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
4786 SetWindowText(hDlg, commentTitle);
4790 SetFocus(GetDlgItem(hDlg, IDOK));
4792 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
4793 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
4794 MAKELPARAM(FALSE, 0));
4795 /* Size and position the dialog */
4796 if (!commentDialog) {
4797 commentDialog = hDlg;
4798 flags = SWP_NOZORDER;
4799 GetClientRect(hDlg, &rect);
4801 sizeY = rect.bottom;
4802 if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&
4803 commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {
4805 EnsureOnScreen(&commentX, &commentY);
4806 wp.length = sizeof(WINDOWPLACEMENT);
4808 wp.showCmd = SW_SHOW;
4809 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
4810 wp.rcNormalPosition.left = commentX;
4811 wp.rcNormalPosition.right = commentX + commentW;
4812 wp.rcNormalPosition.top = commentY;
4813 wp.rcNormalPosition.bottom = commentY + commentH;
4814 SetWindowPlacement(hDlg, &wp);
4816 GetClientRect(hDlg, &rect);
4817 newSizeX = rect.right;
4818 newSizeY = rect.bottom;
4819 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
4820 newSizeX, newSizeY);
4827 case WM_COMMAND: /* message: received a command */
4828 switch (LOWORD(wParam)) {
4832 /* Read changed options from the dialog box */
4833 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4834 len = GetWindowTextLength(hwndText);
4835 str = (char *) malloc(len + 1);
4836 GetWindowText(hwndText, str, len + 1);
4845 ReplaceComment(commentIndex, str);
4852 case OPT_CancelComment:
4856 case OPT_ClearComment:
4857 SetDlgItemText(hDlg, OPT_CommentText, "");
4860 case OPT_EditComment:
4870 newSizeX = LOWORD(lParam);
4871 newSizeY = HIWORD(lParam);
4872 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
4877 case WM_GETMINMAXINFO:
4878 /* Prevent resizing window too small */
4879 mmi = (MINMAXINFO *) lParam;
4880 mmi->ptMinTrackSize.x = 100;
4881 mmi->ptMinTrackSize.y = 100;
4888 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
4893 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
4895 if (str == NULL) str = "";
4896 p = (char *) malloc(2 * strlen(str) + 2);
4899 if (*str == '\n') *q++ = '\r';
4903 if (commentText != NULL) free(commentText);
4905 commentIndex = index;
4906 commentTitle = title;
4910 if (commentDialog) {
4911 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
4912 if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);
4914 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
4915 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
4916 hwndMain, (DLGPROC)lpProc);
4917 FreeProcInstance(lpProc);
4919 commentDialogUp = TRUE;
4923 /*---------------------------------------------------------------------------*\
4925 * Type-in move dialog functions
4927 \*---------------------------------------------------------------------------*/
4930 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4935 int fromX, fromY, toX, toY;
4940 move[0] = (char) lParam;
4942 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
4943 hInput = GetDlgItem(hDlg, OPT_Move);
4944 SetWindowText(hInput, move);
4946 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
4950 switch (LOWORD(wParam)) {
4952 if (gameMode != EditGame && currentMove != forwardMostMove &&
4953 gameMode != Training) {
4954 DisplayMoveError("Displayed move is not current");
4956 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
4957 if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
4958 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
4959 if (gameMode != Training)
4960 forwardMostMove = currentMove;
4961 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4963 DisplayMoveError("Could not parse move");
4966 EndDialog(hDlg, TRUE);
4969 EndDialog(hDlg, FALSE);
4980 PopUpMoveDialog(char firstchar)
4984 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
4985 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
4986 gameMode == AnalyzeMode || gameMode == EditGame ||
4987 gameMode == EditPosition || gameMode == IcsExamining ||
4988 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
4989 gameMode == Training) {
4990 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
4991 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
4992 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
4993 FreeProcInstance(lpProc);
4997 /*---------------------------------------------------------------------------*\
5001 \*---------------------------------------------------------------------------*/
5003 /* Nonmodal error box */
5004 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
5005 WPARAM wParam, LPARAM lParam);
5008 ErrorPopUp(char *title, char *content)
5012 BOOLEAN modal = hwndMain == NULL;
5030 strncpy(errorTitle, title, sizeof(errorTitle));
5031 errorTitle[sizeof(errorTitle) - 1] = '\0';
5034 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
5036 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
5037 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
5038 hwndMain, (DLGPROC)lpProc);
5039 FreeProcInstance(lpProc);
5046 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
5047 if (errorDialog == NULL) return;
5048 DestroyWindow(errorDialog);
5053 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5060 GetWindowRect(hDlg, &rChild);
5061 SetWindowPos(hDlg, NULL, rChild.left,
5062 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
5063 0, 0, SWP_NOZORDER|SWP_NOSIZE);
5065 SetWindowText(hDlg, errorTitle);
5066 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
5067 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
5071 switch (LOWORD(wParam)) {
5074 if (errorDialog == hDlg) errorDialog = NULL;
5075 DestroyWindow(hDlg);
5086 /*---------------------------------------------------------------------------*\
5088 * Ics Interaction console functions
5090 \*---------------------------------------------------------------------------*/
5092 #define HISTORY_SIZE 64
5093 static char *history[HISTORY_SIZE];
5094 int histIn = 0, histP = 0;
5097 SaveInHistory(char *cmd)
5099 if (history[histIn] != NULL) {
5100 free(history[histIn]);
5101 history[histIn] = NULL;
5103 if (*cmd == NULLCHAR) return;
5104 history[histIn] = StrSave(cmd);
5105 histIn = (histIn + 1) % HISTORY_SIZE;
5106 if (history[histIn] != NULL) {
5107 free(history[histIn]);
5108 history[histIn] = NULL;
5114 PrevInHistory(char *cmd)
5117 if (histP == histIn) {
5118 if (history[histIn] != NULL) free(history[histIn]);
5119 history[histIn] = StrSave(cmd);
5121 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
5122 if (newhp == histIn || history[newhp] == NULL) return NULL;
5124 return history[histP];
5130 if (histP == histIn) return NULL;
5131 histP = (histP + 1) % HISTORY_SIZE;
5132 return history[histP];
5141 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
5142 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];
5145 ParseIcsTextMenu(char *icsTextMenuString)
5148 IcsTextMenuEntry *e = icsTextMenuEntry;
5149 char *p = icsTextMenuString;
5150 while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5153 if (e->command != NULL) {
5159 e = icsTextMenuEntry;
5160 while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5161 if (*p == ';' || *p == '\n') {
5162 e->item = strdup("-");
5165 } else if (*p == '-') {
5166 e->item = strdup("-");
5171 char *q, *r, *s, *t;
5174 if (q == NULL) break;
5176 r = strchr(q + 1, ',');
5177 if (r == NULL) break;
5179 s = strchr(r + 1, ',');
5180 if (s == NULL) break;
5183 t = strchr(s + 1, c);
5186 t = strchr(s + 1, c);
5188 if (t != NULL) *t = NULLCHAR;
5189 e->item = strdup(p);
5190 e->command = strdup(q + 1);
5191 e->getname = *(r + 1) != '0';
5192 e->immediate = *(s + 1) != '0';
5196 if (t == NULL) break;
5205 LoadIcsTextMenu(IcsTextMenuEntry *e)
5209 hmenu = LoadMenu(hInst, "TextMenu");
5210 h = GetSubMenu(hmenu, 0);
5212 if (strcmp(e->item, "-") == 0) {
5213 AppendMenu(h, MF_SEPARATOR, 0, 0);
5215 if (e->item[0] == '|') {
5216 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
5217 IDM_CommandX + i, &e->item[1]);
5219 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
5228 WNDPROC consoleTextWindowProc;
5231 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
5233 char buf[MSG_SIZ], name[MSG_SIZ];
5234 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5238 SetWindowText(hInput, command);
5240 SendMessage(hInput, WM_CHAR, '\r', 0);
5244 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5249 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5250 if (sel.cpMin == sel.cpMax) {
5251 /* Expand to surrounding word */
5254 tr.chrg.cpMax = sel.cpMin;
5255 tr.chrg.cpMin = --sel.cpMin;
5256 if (sel.cpMin < 0) break;
5257 tr.lpstrText = name;
5258 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5259 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5263 tr.chrg.cpMin = sel.cpMax;
5264 tr.chrg.cpMax = ++sel.cpMax;
5265 tr.lpstrText = name;
5266 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
5267 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5270 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5271 MessageBeep(MB_ICONEXCLAMATION);
5275 tr.lpstrText = name;
5276 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5278 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5279 MessageBeep(MB_ICONEXCLAMATION);
5282 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
5285 sprintf(buf, "%s %s", command, name);
5286 SetWindowText(hInput, buf);
5287 SendMessage(hInput, WM_CHAR, '\r', 0);
5289 sprintf(buf, "%s %s ", command, name); /* trailing space */
5290 SetWindowText(hInput, buf);
5293 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5299 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5306 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5309 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
5314 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5315 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
5320 if (wParam == '\t') {
5321 if (GetKeyState(VK_SHIFT) < 0) {
5323 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5324 if (buttonDesc[0].hwnd) {
5325 SetFocus(buttonDesc[0].hwnd);
5331 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
5334 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5336 SendMessage(hInput, message, wParam, lParam);
5340 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5342 return SendMessage(hInput, message, wParam, lParam);
5343 case WM_MBUTTONDOWN:
5344 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5345 case WM_RBUTTONDOWN:
5346 if (!(GetKeyState(VK_SHIFT) & ~1)) {
5347 /* Move selection here if it was empty */
5349 pt.x = LOWORD(lParam);
5350 pt.y = HIWORD(lParam);
5351 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5352 if (sel.cpMin == sel.cpMax) {
5353 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
5354 sel.cpMax = sel.cpMin;
5355 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5357 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
5361 if (GetKeyState(VK_SHIFT) & ~1) {
5362 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5363 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5366 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
5367 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5368 if (sel.cpMin == sel.cpMax) {
5369 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5370 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
5372 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5373 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5375 pt.x = LOWORD(lParam);
5376 pt.y = HIWORD(lParam);
5377 MenuPopup(hwnd, pt, hmenu, -1);
5381 switch (LOWORD(wParam)) {
5382 case IDM_QuickPaste:
5384 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5385 if (sel.cpMin == sel.cpMax) {
5386 MessageBeep(MB_ICONEXCLAMATION);
5389 SendMessage(hwnd, WM_COPY, 0, 0);
5390 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5391 SendMessage(hInput, WM_PASTE, 0, 0);
5396 SendMessage(hwnd, WM_CUT, 0, 0);
5399 SendMessage(hwnd, WM_PASTE, 0, 0);
5402 SendMessage(hwnd, WM_COPY, 0, 0);
5406 int i = LOWORD(wParam) - IDM_CommandX;
5407 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
5408 icsTextMenuEntry[i].command != NULL) {
5409 CommandX(hwnd, icsTextMenuEntry[i].command,
5410 icsTextMenuEntry[i].getname,
5411 icsTextMenuEntry[i].immediate);
5419 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
5422 WNDPROC consoleInputWindowProc;
5425 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5429 static BOOL sendNextChar = FALSE;
5430 static BOOL quoteNextChar = FALSE;
5431 InputSource *is = consoleInputSource;
5437 if (!appData.localLineEditing || sendNextChar) {
5438 is->buf[0] = (CHAR) wParam;
5440 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5441 sendNextChar = FALSE;
5444 if (quoteNextChar) {
5445 buf[0] = (char) wParam;
5447 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
5448 quoteNextChar = FALSE;
5452 case '\r': /* Enter key */
5453 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
5454 if (consoleEcho) SaveInHistory(is->buf);
5455 is->buf[is->count++] = '\n';
5456 is->buf[is->count] = NULLCHAR;
5457 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5459 ConsoleOutput(is->buf, is->count, TRUE);
5460 } else if (appData.localLineEditing) {
5461 ConsoleOutput("\n", 1, TRUE);
5464 case '\033': /* Escape key */
5465 SetWindowText(hwnd, "");
5466 cf.cbSize = sizeof(CHARFORMAT);
5467 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
5469 cf.crTextColor = textAttribs[ColorNormal].color;
5471 cf.crTextColor = COLOR_ECHOOFF;
5473 cf.dwEffects = textAttribs[ColorNormal].effects;
5474 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
5476 case '\t': /* Tab key */
5477 if (GetKeyState(VK_SHIFT) < 0) {
5479 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
5482 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5483 if (buttonDesc[0].hwnd) {
5484 SetFocus(buttonDesc[0].hwnd);
5490 case '\023': /* Ctrl+S */
5491 sendNextChar = TRUE;
5493 case '\021': /* Ctrl+Q */
5494 quoteNextChar = TRUE;
5503 GetWindowText(hwnd, buf, MSG_SIZ);
5504 p = PrevInHistory(buf);
5506 SetWindowText(hwnd, p);
5509 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5514 p = NextInHistory();
5516 SetWindowText(hwnd, p);
5519 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5525 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5529 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
5533 case WM_MBUTTONDOWN:
5534 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5535 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5538 if (GetKeyState(VK_SHIFT) & ~1) {
5539 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5540 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5544 hmenu = LoadMenu(hInst, "InputMenu");
5545 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5546 if (sel.cpMin == sel.cpMax) {
5547 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5548 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
5550 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5551 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5553 pt.x = LOWORD(lParam);
5554 pt.y = HIWORD(lParam);
5555 MenuPopup(hwnd, pt, hmenu, -1);
5559 switch (LOWORD(wParam)) {
5561 SendMessage(hwnd, EM_UNDO, 0, 0);
5565 sel.cpMax = -1; /*999999?*/
5566 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5569 SendMessage(hwnd, WM_CUT, 0, 0);
5572 SendMessage(hwnd, WM_PASTE, 0, 0);
5575 SendMessage(hwnd, WM_COPY, 0, 0);
5580 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
5583 #define CO_MAX 100000
5584 #define CO_TRIM 1000
5587 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5589 static HWND hText, hInput, hFocus;
5590 InputSource *is = consoleInputSource;
5592 static int sizeX, sizeY;
5593 int newSizeX, newSizeY;
5597 case WM_INITDIALOG: /* message: initialize dialog box */
5599 hText = GetDlgItem(hDlg, OPT_ConsoleText);
5600 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
5602 consoleTextWindowProc = (WNDPROC)
5603 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
5604 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5605 consoleInputWindowProc = (WNDPROC)
5606 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
5607 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5608 Colorize(ColorNormal, TRUE);
5609 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
5610 ChangedConsoleFont();
5611 GetClientRect(hDlg, &rect);
5613 sizeY = rect.bottom;
5614 if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&
5615 consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {
5617 EnsureOnScreen(&consoleX, &consoleY);
5618 wp.length = sizeof(WINDOWPLACEMENT);
5620 wp.showCmd = SW_SHOW;
5621 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
5622 wp.rcNormalPosition.left = consoleX;
5623 wp.rcNormalPosition.right = consoleX + consoleW;
5624 wp.rcNormalPosition.top = consoleY;
5625 wp.rcNormalPosition.bottom = consoleY + consoleH;
5626 SetWindowPlacement(hDlg, &wp);
5640 if (IsIconic(hDlg)) break;
5641 newSizeX = LOWORD(lParam);
5642 newSizeY = HIWORD(lParam);
5643 if (sizeX != newSizeX || sizeY != newSizeY) {
5644 RECT rectText, rectInput;
5646 int newTextHeight, newTextWidth;
5647 GetWindowRect(hText, &rectText);
5648 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
5649 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
5650 if (newTextHeight < 0) {
5651 newSizeY += -newTextHeight;
5654 SetWindowPos(hText, NULL, 0, 0,
5655 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
5656 GetWindowRect(hInput, &rectInput); /* gives screen coords */
5657 pt.x = rectInput.left;
5658 pt.y = rectInput.top + newSizeY - sizeY;
5659 ScreenToClient(hDlg, &pt);
5660 SetWindowPos(hInput, NULL,
5661 pt.x, pt.y, /* needs client coords */
5662 rectInput.right - rectInput.left + newSizeX - sizeX,
5663 rectInput.bottom - rectInput.top, SWP_NOZORDER);
5669 case WM_GETMINMAXINFO:
5670 /* Prevent resizing window too small */
5671 mmi = (MINMAXINFO *) lParam;
5672 mmi->ptMinTrackSize.x = 100;
5673 mmi->ptMinTrackSize.y = 100;
5676 return DefWindowProc(hDlg, message, wParam, lParam);
5684 if (hwndConsole) return;
5685 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
5686 SendMessage(hCons, WM_INITDIALOG, 0, 0);
5691 ConsoleOutput(char* data, int length, int forceVisible)
5699 static int delayLF = 0;
5700 CHARRANGE savesel, sel;
5702 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
5718 } else if (*p == '\007') {
5719 MyPlaySound(&sounds[(int)SoundBell]);
5726 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
5727 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5728 /* Save current selection */
5729 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
5730 exlen = GetWindowTextLength(hText);
5731 /* Find out whether current end of text is visible */
5732 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
5733 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
5734 /* Trim existing text if it's too long */
5735 if (exlen + (q - buf) > CO_MAX) {
5736 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
5739 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5740 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
5742 savesel.cpMin -= trim;
5743 savesel.cpMax -= trim;
5744 if (exlen < 0) exlen = 0;
5745 if (savesel.cpMin < 0) savesel.cpMin = 0;
5746 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
5748 /* Append the new text */
5751 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5752 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
5753 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
5754 if (forceVisible || exlen == 0 ||
5755 (rect.left <= pEnd.x && pEnd.x < rect.right &&
5756 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
5757 /* Scroll to make new end of text visible if old end of text
5758 was visible or new text is an echo of user typein */
5759 sel.cpMin = 9999999;
5760 sel.cpMax = 9999999;
5761 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5762 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5763 SendMessage(hText, EM_SCROLLCARET, 0, 0);
5764 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5766 if (savesel.cpMax == exlen || forceVisible) {
5767 /* Move insert point to new end of text if it was at the old
5768 end of text or if the new text is an echo of user typein */
5769 sel.cpMin = 9999999;
5770 sel.cpMax = 9999999;
5771 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5773 /* Restore previous selection */
5774 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
5776 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5783 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
5784 RECT *rect, char *color)
5788 COLORREF oldFg, oldBg;
5791 if (appData.clockMode) {
5793 sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));
5795 sprintf(buf, "%s: %s", color, TimeString(timeRemaining));
5802 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
5803 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
5805 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
5806 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
5808 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
5810 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
5811 rect->top, ETO_CLIPPED|ETO_OPAQUE,
5812 rect, str, strlen(str), NULL);
5814 (void) SetTextColor(hdc, oldFg);
5815 (void) SetBkColor(hdc, oldBg);
5816 (void) SelectObject(hdc, oldFont);
5821 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5826 ResetEvent(ovl->hEvent);
5827 ovl->Offset = ovl->OffsetHigh = 0;
5828 ok = ReadFile(hFile, buf, count, outCount, ovl);
5832 err = GetLastError();
5833 if (err == ERROR_IO_PENDING) {
5834 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5838 err = GetLastError();
5845 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5850 ResetEvent(ovl->hEvent);
5851 ovl->Offset = ovl->OffsetHigh = 0;
5852 ok = WriteFile(hFile, buf, count, outCount, ovl);
5856 err = GetLastError();
5857 if (err == ERROR_IO_PENDING) {
5858 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5862 err = GetLastError();
5870 InputThread(LPVOID arg)
5875 is = (InputSource *) arg;
5876 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
5877 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
5878 while (is->hThread != NULL) {
5879 is->error = DoReadFile(is->hFile, is->next,
5880 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5882 if (is->error == NO_ERROR) {
5883 is->next += is->count;
5885 if (is->error == ERROR_BROKEN_PIPE) {
5886 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5889 is->count = (DWORD) -1;
5892 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5893 if (is->count <= 0) break; /* Quit on EOF or error */
5895 CloseHandle(ovl.hEvent);
5896 CloseHandle(is->hFile);
5901 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
5903 NonOvlInputThread(LPVOID arg)
5910 is = (InputSource *) arg;
5911 while (is->hThread != NULL) {
5912 is->error = ReadFile(is->hFile, is->next,
5913 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5914 &is->count, NULL) ? NO_ERROR : GetLastError();
5915 if (is->error == NO_ERROR) {
5916 /* Change CRLF to LF */
5917 if (is->next > is->buf) {
5927 if (prev == '\r' && *p == '\n') {
5939 if (is->error == ERROR_BROKEN_PIPE) {
5940 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5943 is->count = (DWORD) -1;
5946 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5947 if (is->count < 0) break; /* Quit on error */
5949 CloseHandle(is->hFile);
5954 SocketInputThread(LPVOID arg)
5958 is = (InputSource *) arg;
5959 while (is->hThread != NULL) {
5960 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
5961 if ((int)is->count == SOCKET_ERROR) {
5962 is->count = (DWORD) -1;
5963 is->error = WSAGetLastError();
5965 is->error = NO_ERROR;
5966 is->next += is->count;
5967 if (is->count == 0 && is->second == is) {
5968 /* End of file on stderr; quit with no message */
5972 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5973 if (is->count <= 0) break; /* Quit on EOF or error */
5979 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5983 is = (InputSource *) lParam;
5984 if (is->lineByLine) {
5985 /* Feed in lines one by one */
5988 while (q < is->next) {
5990 (is->func)(is, is->closure, p, q - p, NO_ERROR);
5994 /* Move any partial line to the start of the buffer */
5996 while (p < is->next) {
6000 if (is->error != NO_ERROR || is->count == 0) {
6001 /* Notify backend of the error. Note: If there was a partial
6002 line at the end, it is not flushed through. */
6003 (is->func)(is, is->closure, is->buf, is->count, is->error);
6006 /* Feed in the whole chunk of input at once */
6007 (is->func)(is, is->closure, is->buf, is->count, is->error);
6012 /*---------------------------------------------------------------------------*\
6014 * Menu enables. Used when setting various modes.
6016 \*---------------------------------------------------------------------------*/
6024 SetMenuEnables(HMENU hmenu, Enables *enab)
6026 while (enab->item > 0) {
6027 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
6032 Enables gnuEnables[] = {
6033 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6034 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6035 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6036 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
6037 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
6038 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
6039 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6040 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
6041 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
6042 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6046 Enables icsEnables[] = {
6047 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6048 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6049 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6050 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6051 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6052 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6053 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6054 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6055 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6056 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6057 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6058 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
6063 Enables zippyEnables[] = {
6064 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6065 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
6066 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
6071 Enables ncpEnables[] = {
6072 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6073 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6074 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6075 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6076 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6077 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6078 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6079 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6080 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
6081 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6082 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6083 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6084 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6085 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6086 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6090 Enables trainingOnEnables[] = {
6091 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
6092 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
6093 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
6094 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
6095 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
6096 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
6097 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6098 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
6102 Enables trainingOffEnables[] = {
6103 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
6104 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
6105 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
6106 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
6107 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
6108 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
6109 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6110 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
6114 /* These modify either ncpEnables or gnuEnables */
6115 Enables cmailEnables[] = {
6116 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
6117 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
6118 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
6119 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
6120 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
6121 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6122 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
6126 Enables machineThinkingEnables[] = {
6127 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
6128 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
6129 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
6130 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
6131 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
6132 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
6133 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
6134 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
6135 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
6136 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
6137 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6138 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6139 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6140 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
6141 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6145 Enables userThinkingEnables[] = {
6146 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
6147 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
6148 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
6149 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
6150 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
6151 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
6152 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
6153 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
6154 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
6155 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
6156 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
6157 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
6158 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
6159 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
6160 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
6164 /*---------------------------------------------------------------------------*\
6166 * Front-end interface functions exported by XBoard.
6167 * Functions appear in same order as prototypes in frontend.h.
6169 \*---------------------------------------------------------------------------*/
6173 static UINT prevChecked = 0;
6174 static int prevPausing = 0;
6177 if (pausing != prevPausing) {
6178 prevPausing = pausing;
6179 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
6180 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
6181 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
6185 case BeginningOfGame:
6186 if (appData.icsActive)
6187 nowChecked = IDM_IcsClient;
6188 else if (appData.noChessProgram)
6189 nowChecked = IDM_EditGame;
6191 nowChecked = IDM_MachineBlack;
6193 case MachinePlaysBlack:
6194 nowChecked = IDM_MachineBlack;
6196 case MachinePlaysWhite:
6197 nowChecked = IDM_MachineWhite;
6199 case TwoMachinesPlay:
6200 nowChecked = IDM_TwoMachines;
6203 nowChecked = IDM_AnalysisMode;
6206 nowChecked = IDM_AnalyzeFile;
6209 nowChecked = IDM_EditGame;
6211 case PlayFromGameFile:
6212 nowChecked = IDM_LoadGame;
6215 nowChecked = IDM_EditPosition;
6218 nowChecked = IDM_Training;
6220 case IcsPlayingWhite:
6221 case IcsPlayingBlack:
6224 nowChecked = IDM_IcsClient;
6231 if (prevChecked != 0)
6232 (void) CheckMenuItem(GetMenu(hwndMain),
6233 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
6234 if (nowChecked != 0)
6235 (void) CheckMenuItem(GetMenu(hwndMain),
6236 nowChecked, MF_BYCOMMAND|MF_CHECKED);
6238 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
6239 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
6240 MF_BYCOMMAND|MF_ENABLED);
6242 (void) EnableMenuItem(GetMenu(hwndMain),
6243 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
6246 prevChecked = nowChecked;
6248 /* icsEngineAnalyze - Do a sceure check too */
6249 if (appData.icsActive) {
6250 if (appData.icsEngineAnalyze) {
6251 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6252 MF_BYCOMMAND|MF_CHECKED);
6254 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6255 MF_BYCOMMAND|MF_UNCHECKED);
6263 HMENU hmenu = GetMenu(hwndMain);
6264 SetMenuEnables(hmenu, icsEnables);
6265 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
6266 MF_BYPOSITION|MF_ENABLED);
6268 if (appData.zippyPlay) {
6269 SetMenuEnables(hmenu, zippyEnables);
6270 /* icsEngineAnalyze */
6271 if (!appData.noChessProgram)
6272 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6273 MF_BYCOMMAND|MF_ENABLED);
6281 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
6287 HMENU hmenu = GetMenu(hwndMain);
6288 SetMenuEnables(hmenu, ncpEnables);
6289 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
6290 MF_BYPOSITION|MF_GRAYED);
6291 DrawMenuBar(hwndMain);
6297 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
6304 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
6305 for (i = 0; i < N_BUTTONS; i++) {
6306 if (buttonDesc[i].hwnd != NULL)
6307 EnableWindow(buttonDesc[i].hwnd, FALSE);
6312 VOID SetTrainingModeOff()
6315 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
6316 for (i = 0; i < N_BUTTONS; i++) {
6317 if (buttonDesc[i].hwnd != NULL)
6318 EnableWindow(buttonDesc[i].hwnd, TRUE);
6324 SetUserThinkingEnables()
6326 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
6330 SetMachineThinkingEnables()
6332 HMENU hMenu = GetMenu(hwndMain);
6333 int flags = MF_BYCOMMAND|MF_ENABLED;
6335 SetMenuEnables(hMenu, machineThinkingEnables);
6337 if (gameMode == MachinePlaysBlack) {
6338 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
6339 } else if (gameMode == MachinePlaysWhite) {
6340 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
6341 } else if (gameMode == TwoMachinesPlay) {
6342 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
6348 DisplayTitle(char *str)
6350 char title[MSG_SIZ], *host;
6351 if (str[0] != NULLCHAR) {
6353 } else if (appData.icsActive) {
6354 if (appData.icsCommPort[0] != NULLCHAR)
6357 host = appData.icsHost;
6358 sprintf(title, "%s: %s", szTitle, host);
6359 } else if (appData.noChessProgram) {
6360 strcpy(title, szTitle);
6362 strcpy(title, szTitle);
6363 strcat(title, ": ");
6364 strcat(title, first.tidy);
6366 SetWindowText(hwndMain, title);
6371 DisplayMessage(char *str1, char *str2)
6375 int remain = MESSAGE_TEXT_MAX - 1;
6378 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
6379 messageText[0] = NULLCHAR;
6382 if (len > remain) len = remain;
6383 strncpy(messageText, str1, len);
6384 messageText[len] = NULLCHAR;
6387 if (*str2 && remain >= 2) {
6389 strcat(messageText, " ");
6393 if (len > remain) len = remain;
6394 strncat(messageText, str2, len);
6396 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
6398 if (IsIconic(hwndMain)) return;
6399 hdc = GetDC(hwndMain);
6400 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
6401 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
6402 &messageRect, messageText, strlen(messageText), NULL);
6403 (void) SelectObject(hdc, oldFont);
6404 (void) ReleaseDC(hwndMain, hdc);
6408 DisplayError(char *str, int error)
6410 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
6416 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6417 NULL, error, LANG_NEUTRAL,
6418 (LPSTR) buf2, MSG_SIZ, NULL);
6420 sprintf(buf, "%s:\n%s", str, buf2);
6422 ErrorMap *em = errmap;
6423 while (em->err != 0 && em->err != error) em++;
6425 sprintf(buf, "%s:\n%s", str, em->msg);
6427 sprintf(buf, "%s:\nError code %d", str, error);
6432 ErrorPopUp("Error", buf);
6437 DisplayMoveError(char *str)
6441 DrawPosition(FALSE, NULL);
6442 if (appData.popupMoveErrors) {
6443 ErrorPopUp("Error", str);
6445 DisplayMessage(str, "");
6446 moveErrorMessageUp = TRUE;
6451 DisplayFatalError(char *str, int error, int exitStatus)
6453 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
6455 char *label = exitStatus ? "Fatal Error" : "Exiting";
6458 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6459 NULL, error, LANG_NEUTRAL,
6460 (LPSTR) buf2, MSG_SIZ, NULL);
6462 sprintf(buf, "%s:\n%s", str, buf2);
6464 ErrorMap *em = errmap;
6465 while (em->err != 0 && em->err != error) em++;
6467 sprintf(buf, "%s:\n%s", str, em->msg);
6469 sprintf(buf, "%s:\nError code %d", str, error);
6474 if (appData.debugMode) {
6475 fprintf(debugFP, "%s: %s\n", label, str);
6477 if (appData.popupExitMessage) {
6478 (void) MessageBox(hwndMain, str, label, MB_OK|
6479 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
6481 ExitEvent(exitStatus);
6486 DisplayInformation(char *str)
6488 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
6493 DisplayNote(char *str)
6495 ErrorPopUp("Note", str);
6500 char *title, *question, *replyPrefix;
6505 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
6507 static QuestionParams *qp;
6508 char reply[MSG_SIZ];
6513 qp = (QuestionParams *) lParam;
6514 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
6515 SetWindowText(hDlg, qp->title);
6516 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
6517 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
6521 switch (LOWORD(wParam)) {
6523 strcpy(reply, qp->replyPrefix);
6524 if (*reply) strcat(reply, " ");
6525 len = strlen(reply);
6526 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
6527 strcat(reply, "\n");
6528 OutputToProcess(qp->pr, reply, strlen(reply), &err);
6529 EndDialog(hDlg, TRUE);
6530 if (err) DisplayFatalError("Error writing to chess program", err, 1);
6533 EndDialog(hDlg, FALSE);
6544 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
6550 qp.question = question;
6551 qp.replyPrefix = replyPrefix;
6553 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
6554 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
6555 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
6556 FreeProcInstance(lpProc);
6561 DisplayIcsInteractionTitle(char *str)
6563 char consoleTitle[MSG_SIZ];
6565 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
6566 SetWindowText(hwndConsole, consoleTitle);
6570 DrawPosition(int fullRedraw, Board board)
6572 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
6580 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
6581 dragInfo.pos.x = dragInfo.pos.y = -1;
6582 dragInfo.pos.x = dragInfo.pos.y = -1;
6583 dragInfo.lastpos = dragInfo.pos;
6584 dragInfo.start.x = dragInfo.start.y = -1;
6585 dragInfo.from = dragInfo.start;
6587 DrawPosition(TRUE, NULL);
6593 CommentPopUp(char *title, char *str)
6595 HWND hwnd = GetActiveWindow();
6596 EitherCommentPopUp(0, title, str, FALSE);
6597 SetActiveWindow(hwnd);
6601 CommentPopDown(void)
6603 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
6604 if (commentDialog) {
6605 ShowWindow(commentDialog, SW_HIDE);
6607 commentDialogUp = FALSE;
6611 EditCommentPopUp(int index, char *title, char *str)
6613 EitherCommentPopUp(index, title, str, TRUE);
6620 MyPlaySound(&sounds[(int)SoundMove]);
6623 VOID PlayIcsWinSound()
6625 MyPlaySound(&sounds[(int)SoundIcsWin]);
6628 VOID PlayIcsLossSound()
6630 MyPlaySound(&sounds[(int)SoundIcsLoss]);
6633 VOID PlayIcsDrawSound()
6635 MyPlaySound(&sounds[(int)SoundIcsDraw]);
6638 VOID PlayIcsUnfinishedSound()
6640 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
6646 MyPlaySound(&sounds[(int)SoundAlarm]);
6655 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6656 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
6657 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
6666 consoleEcho = FALSE;
6667 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6668 /* This works OK: set text and background both to the same color */
6670 cf.crTextColor = COLOR_ECHOOFF;
6671 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
6672 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
6677 void Colorize(ColorClass cc, int continuation)
6679 currentColorClass = cc;
6680 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
6681 consoleCF.crTextColor = textAttribs[cc].color;
6682 consoleCF.dwEffects = textAttribs[cc].effects;
6683 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
6689 static char buf[MSG_SIZ];
6690 DWORD bufsiz = MSG_SIZ;
6692 if (!GetUserName(buf, &bufsiz)) {
6693 /*DisplayError("Error getting user name", GetLastError());*/
6694 strcpy(buf, "User");
6702 static char buf[MSG_SIZ];
6703 DWORD bufsiz = MSG_SIZ;
6705 if (!GetComputerName(buf, &bufsiz)) {
6706 /*DisplayError("Error getting host name", GetLastError());*/
6707 strcpy(buf, "Unknown");
6716 return clockTimerEvent != 0;
6722 if (clockTimerEvent == 0) return FALSE;
6723 KillTimer(hwndMain, clockTimerEvent);
6724 clockTimerEvent = 0;
6729 StartClockTimer(long millisec)
6731 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
6732 (UINT) millisec, NULL);
6736 DisplayWhiteClock(long timeRemaining, int highlight)
6739 hdc = GetDC(hwndMain);
6740 if (!IsIconic(hwndMain)) {
6741 DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");
6743 if (highlight && iconCurrent == iconBlack) {
6744 iconCurrent = iconWhite;
6745 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6746 if (IsIconic(hwndMain)) {
6747 DrawIcon(hdc, 2, 2, iconCurrent);
6750 (void) ReleaseDC(hwndMain, hdc);
6752 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6756 DisplayBlackClock(long timeRemaining, int highlight)
6759 hdc = GetDC(hwndMain);
6760 if (!IsIconic(hwndMain)) {
6761 DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");
6763 if (highlight && iconCurrent == iconWhite) {
6764 iconCurrent = iconBlack;
6765 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6766 if (IsIconic(hwndMain)) {
6767 DrawIcon(hdc, 2, 2, iconCurrent);
6770 (void) ReleaseDC(hwndMain, hdc);
6772 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6777 LoadGameTimerRunning()
6779 return loadGameTimerEvent != 0;
6785 if (loadGameTimerEvent == 0) return FALSE;
6786 KillTimer(hwndMain, loadGameTimerEvent);
6787 loadGameTimerEvent = 0;
6792 StartLoadGameTimer(long millisec)
6794 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
6795 (UINT) millisec, NULL);
6803 char fileTitle[MSG_SIZ];
6805 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
6806 f = OpenFileDialog(hwndMain, TRUE, defName,
6807 appData.oldSaveStyle ? "gam" : "pgn",
6809 "Save Game to File", NULL, fileTitle, NULL);
6818 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
6820 if (delayedTimerEvent != 0) {
6821 if (appData.debugMode) {
6822 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
6824 KillTimer(hwndMain, delayedTimerEvent);
6825 delayedTimerEvent = 0;
6826 delayedTimerCallback();
6828 delayedTimerCallback = cb;
6829 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
6830 (UINT) millisec, NULL);
6833 DelayedEventCallback
6836 if (delayedTimerEvent) {
6837 return delayedTimerCallback;
6844 CancelDelayedEvent()
6846 if (delayedTimerEvent) {
6847 KillTimer(hwndMain, delayedTimerEvent);
6848 delayedTimerEvent = 0;
6852 /* Start a child process running the given program.
6853 The process's standard output can be read from "from", and its
6854 standard input can be written to "to".
6855 Exit with fatal error if anything goes wrong.
6856 Returns an opaque pointer that can be used to destroy the process
6860 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
6862 #define BUFSIZE 4096
6864 HANDLE hChildStdinRd, hChildStdinWr,
6865 hChildStdoutRd, hChildStdoutWr;
6866 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
6867 SECURITY_ATTRIBUTES saAttr;
6869 PROCESS_INFORMATION piProcInfo;
6870 STARTUPINFO siStartInfo;
6875 if (appData.debugMode) {
6876 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
6881 /* Set the bInheritHandle flag so pipe handles are inherited. */
6882 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
6883 saAttr.bInheritHandle = TRUE;
6884 saAttr.lpSecurityDescriptor = NULL;
6887 * The steps for redirecting child's STDOUT:
6888 * 1. Create anonymous pipe to be STDOUT for child.
6889 * 2. Create a noninheritable duplicate of read handle,
6890 * and close the inheritable read handle.
6893 /* Create a pipe for the child's STDOUT. */
6894 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
6895 return GetLastError();
6898 /* Duplicate the read handle to the pipe, so it is not inherited. */
6899 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
6900 GetCurrentProcess(), &hChildStdoutRdDup, 0,
6901 FALSE, /* not inherited */
6902 DUPLICATE_SAME_ACCESS);
6904 return GetLastError();
6906 CloseHandle(hChildStdoutRd);
6909 * The steps for redirecting child's STDIN:
6910 * 1. Create anonymous pipe to be STDIN for child.
6911 * 2. Create a noninheritable duplicate of write handle,
6912 * and close the inheritable write handle.
6915 /* Create a pipe for the child's STDIN. */
6916 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
6917 return GetLastError();
6920 /* Duplicate the write handle to the pipe, so it is not inherited. */
6921 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
6922 GetCurrentProcess(), &hChildStdinWrDup, 0,
6923 FALSE, /* not inherited */
6924 DUPLICATE_SAME_ACCESS);
6926 return GetLastError();
6928 CloseHandle(hChildStdinWr);
6930 /* Arrange to (1) look in dir for the child .exe file, and
6931 * (2) have dir be the child's working directory. Interpret
6932 * dir relative to the directory WinBoard loaded from. */
6933 GetCurrentDirectory(MSG_SIZ, buf);
6934 SetCurrentDirectory(installDir);
6935 SetCurrentDirectory(dir);
6937 /* Now create the child process. */
6939 siStartInfo.cb = sizeof(STARTUPINFO);
6940 siStartInfo.lpReserved = NULL;
6941 siStartInfo.lpDesktop = NULL;
6942 siStartInfo.lpTitle = NULL;
6943 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
6944 siStartInfo.cbReserved2 = 0;
6945 siStartInfo.lpReserved2 = NULL;
6946 siStartInfo.hStdInput = hChildStdinRd;
6947 siStartInfo.hStdOutput = hChildStdoutWr;
6948 siStartInfo.hStdError = hChildStdoutWr;
6950 fSuccess = CreateProcess(NULL,
6951 cmdLine, /* command line */
6952 NULL, /* process security attributes */
6953 NULL, /* primary thread security attrs */
6954 TRUE, /* handles are inherited */
6955 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
6956 NULL, /* use parent's environment */
6958 &siStartInfo, /* STARTUPINFO pointer */
6959 &piProcInfo); /* receives PROCESS_INFORMATION */
6961 err = GetLastError();
6962 SetCurrentDirectory(buf); /* return to prev directory */
6967 /* Close the handles we don't need in the parent */
6968 CloseHandle(piProcInfo.hThread);
6969 CloseHandle(hChildStdinRd);
6970 CloseHandle(hChildStdoutWr);
6972 /* Prepare return value */
6973 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6975 cp->hProcess = piProcInfo.hProcess;
6976 cp->pid = piProcInfo.dwProcessId;
6977 cp->hFrom = hChildStdoutRdDup;
6978 cp->hTo = hChildStdinWrDup;
6982 /* Klaus Friedel says that this Sleep solves a problem under Windows
6983 2000 where engines sometimes don't see the initial command(s)
6984 from WinBoard and hang. I don't understand how that can happen,
6985 but the Sleep is harmless, so I've put it in. Others have also
6986 reported what may be the same problem, so hopefully this will fix
6995 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
6999 cp = (ChildProc *) pr;
7000 if (cp == NULL) return;
7004 /* TerminateProcess is considered harmful, so... */
7005 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
7006 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
7007 /* The following doesn't work because the chess program
7008 doesn't "have the same console" as WinBoard. Maybe
7009 we could arrange for this even though neither WinBoard
7010 nor the chess program uses a console for stdio? */
7011 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
7012 CloseHandle(cp->hProcess);
7016 if (cp->hFrom) CloseHandle(cp->hFrom);
7020 closesocket(cp->sock);
7025 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
7026 closesocket(cp->sock);
7027 closesocket(cp->sock2);
7035 InterruptChildProcess(ProcRef pr)
7039 cp = (ChildProc *) pr;
7040 if (cp == NULL) return;
7043 /* The following doesn't work because the chess program
7044 doesn't "have the same console" as WinBoard. Maybe
7045 we could arrange for this even though neither WinBoard
7046 nor the chess program uses a console for stdio */
7047 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
7052 /* Can't interrupt */
7056 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
7063 OpenTelnet(char *host, char *port, ProcRef *pr)
7065 char cmdLine[MSG_SIZ];
7067 if (port[0] == NULLCHAR) {
7068 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
7070 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
7072 return StartChildProcess(cmdLine, "", pr);
7076 /* Code to open TCP sockets */
7079 OpenTCP(char *host, char *port, ProcRef *pr)
7084 struct sockaddr_in sa, mysa;
7085 struct hostent FAR *hp;
7086 unsigned short uport;
7087 WORD wVersionRequested;
7090 /* Initialize socket DLL */
7091 wVersionRequested = MAKEWORD(1, 1);
7092 err = WSAStartup(wVersionRequested, &wsaData);
7093 if (err != 0) return err;
7096 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7097 err = WSAGetLastError();
7102 /* Bind local address using (mostly) don't-care values.
7104 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7105 mysa.sin_family = AF_INET;
7106 mysa.sin_addr.s_addr = INADDR_ANY;
7107 uport = (unsigned short) 0;
7108 mysa.sin_port = htons(uport);
7109 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7111 err = WSAGetLastError();
7116 /* Resolve remote host name */
7117 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7118 if (!(hp = gethostbyname(host))) {
7119 unsigned int b0, b1, b2, b3;
7121 err = WSAGetLastError();
7123 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7124 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7125 hp->h_addrtype = AF_INET;
7127 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7128 hp->h_addr_list[0] = (char *) malloc(4);
7129 hp->h_addr_list[0][0] = (char) b0;
7130 hp->h_addr_list[0][1] = (char) b1;
7131 hp->h_addr_list[0][2] = (char) b2;
7132 hp->h_addr_list[0][3] = (char) b3;
7138 sa.sin_family = hp->h_addrtype;
7139 uport = (unsigned short) atoi(port);
7140 sa.sin_port = htons(uport);
7141 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7143 /* Make connection */
7144 if (connect(s, (struct sockaddr *) &sa,
7145 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7146 err = WSAGetLastError();
7151 /* Prepare return value */
7152 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7155 *pr = (ProcRef *) cp;
7161 OpenCommPort(char *name, ProcRef *pr)
7166 char fullname[MSG_SIZ];
7169 sprintf(fullname, "\\\\.\\%s", name);
7171 strcpy(fullname, name);
7173 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
7174 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
7175 if (h == (HANDLE) -1) {
7176 return GetLastError();
7180 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
7182 /* Accumulate characters until a 100ms pause, then parse */
7183 ct.ReadIntervalTimeout = 100;
7184 ct.ReadTotalTimeoutMultiplier = 0;
7185 ct.ReadTotalTimeoutConstant = 0;
7186 ct.WriteTotalTimeoutMultiplier = 0;
7187 ct.WriteTotalTimeoutConstant = 0;
7188 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
7190 /* Prepare return value */
7191 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7195 *pr = (ProcRef *) cp;
7201 OpenLoopback(ProcRef *pr)
7203 DisplayFatalError("Not implemented", 0, 1);
7209 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
7214 struct sockaddr_in sa, mysa;
7215 struct hostent FAR *hp;
7216 unsigned short uport;
7217 WORD wVersionRequested;
7220 char stderrPortStr[MSG_SIZ];
7222 /* Initialize socket DLL */
7223 wVersionRequested = MAKEWORD(1, 1);
7224 err = WSAStartup(wVersionRequested, &wsaData);
7225 if (err != 0) return err;
7227 /* Resolve remote host name */
7228 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7229 if (!(hp = gethostbyname(host))) {
7230 unsigned int b0, b1, b2, b3;
7232 err = WSAGetLastError();
7234 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7235 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7236 hp->h_addrtype = AF_INET;
7238 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7239 hp->h_addr_list[0] = (char *) malloc(4);
7240 hp->h_addr_list[0][0] = (char) b0;
7241 hp->h_addr_list[0][1] = (char) b1;
7242 hp->h_addr_list[0][2] = (char) b2;
7243 hp->h_addr_list[0][3] = (char) b3;
7249 sa.sin_family = hp->h_addrtype;
7250 uport = (unsigned short) 514;
7251 sa.sin_port = htons(uport);
7252 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7254 /* Bind local socket to unused "privileged" port address
7257 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7258 mysa.sin_family = AF_INET;
7259 mysa.sin_addr.s_addr = INADDR_ANY;
7260 for (fromPort = 1023;; fromPort--) {
7263 return WSAEADDRINUSE;
7265 if (s == INVALID_SOCKET) {
7266 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7267 err = WSAGetLastError();
7272 uport = (unsigned short) fromPort;
7273 mysa.sin_port = htons(uport);
7274 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7276 err = WSAGetLastError();
7277 if (err == WSAEADDRINUSE) continue;
7281 if (connect(s, (struct sockaddr *) &sa,
7282 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7283 err = WSAGetLastError();
7284 if (err == WSAEADDRINUSE) {
7295 /* Bind stderr local socket to unused "privileged" port address
7297 s2 = INVALID_SOCKET;
7298 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7299 mysa.sin_family = AF_INET;
7300 mysa.sin_addr.s_addr = INADDR_ANY;
7301 for (fromPort = 1023;; fromPort--) {
7302 if (fromPort == prevStderrPort) continue; // don't reuse port
7304 (void) closesocket(s);
7306 return WSAEADDRINUSE;
7308 if (s2 == INVALID_SOCKET) {
7309 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7310 err = WSAGetLastError();
7316 uport = (unsigned short) fromPort;
7317 mysa.sin_port = htons(uport);
7318 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7320 err = WSAGetLastError();
7321 if (err == WSAEADDRINUSE) continue;
7322 (void) closesocket(s);
7326 if (listen(s2, 1) == SOCKET_ERROR) {
7327 err = WSAGetLastError();
7328 if (err == WSAEADDRINUSE) {
7330 s2 = INVALID_SOCKET;
7333 (void) closesocket(s);
7334 (void) closesocket(s2);
7340 prevStderrPort = fromPort; // remember port used
7341 sprintf(stderrPortStr, "%d", fromPort);
7343 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
7344 err = WSAGetLastError();
7345 (void) closesocket(s);
7346 (void) closesocket(s2);
7351 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
7352 err = WSAGetLastError();
7353 (void) closesocket(s);
7354 (void) closesocket(s2);
7358 if (*user == NULLCHAR) user = UserName();
7359 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
7360 err = WSAGetLastError();
7361 (void) closesocket(s);
7362 (void) closesocket(s2);
7366 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
7367 err = WSAGetLastError();
7368 (void) closesocket(s);
7369 (void) closesocket(s2);
7374 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
7375 err = WSAGetLastError();
7376 (void) closesocket(s);
7377 (void) closesocket(s2);
7381 (void) closesocket(s2); /* Stop listening */
7383 /* Prepare return value */
7384 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7388 *pr = (ProcRef *) cp;
7395 AddInputSource(ProcRef pr, int lineByLine,
7396 InputCallback func, VOIDSTAR closure)
7398 InputSource *is, *is2;
7399 ChildProc *cp = (ChildProc *) pr;
7401 is = (InputSource *) calloc(1, sizeof(InputSource));
7402 is->lineByLine = lineByLine;
7404 is->closure = closure;
7409 consoleInputSource = is;
7411 is->kind = cp->kind;
7414 is->hFile = cp->hFrom;
7415 cp->hFrom = NULL; /* now owned by InputThread */
7417 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
7418 (LPVOID) is, 0, &is->id);
7422 is->hFile = cp->hFrom;
7423 cp->hFrom = NULL; /* now owned by InputThread */
7425 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
7426 (LPVOID) is, 0, &is->id);
7430 is->sock = cp->sock;
7432 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7433 (LPVOID) is, 0, &is->id);
7437 is2 = (InputSource *) calloc(1, sizeof(InputSource));
7439 is->sock = cp->sock;
7441 is2->sock = cp->sock2;
7444 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7445 (LPVOID) is, 0, &is->id);
7447 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7448 (LPVOID) is2, 0, &is2->id);
7452 return (InputSourceRef) is;
7456 RemoveInputSource(InputSourceRef isr)
7460 is = (InputSource *) isr;
7461 is->hThread = NULL; /* tell thread to stop */
7462 CloseHandle(is->hThread);
7463 if (is->second != NULL) {
7464 is->second->hThread = NULL;
7465 CloseHandle(is->second->hThread);
7471 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
7474 int outCount = SOCKET_ERROR;
7475 ChildProc *cp = (ChildProc *) pr;
7476 static OVERLAPPED ovl;
7479 ConsoleOutput(message, count, FALSE);
7483 if (ovl.hEvent == NULL) {
7484 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
7486 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
7491 outCount = send(cp->sock, message, count, 0);
7492 if (outCount == SOCKET_ERROR) {
7493 *outError = WSAGetLastError();
7495 *outError = NO_ERROR;
7500 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
7501 &dOutCount, NULL)) {
7502 *outError = NO_ERROR;
7503 outCount = (int) dOutCount;
7505 *outError = GetLastError();
7510 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
7512 if (*outError == NO_ERROR) {
7513 outCount = (int) dOutCount;
7521 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
7524 /* Ignore delay, not implemented for WinBoard */
7525 return OutputToProcess(pr, message, count, outError);
7530 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
7531 char *buf, int count, int error)
7533 DisplayFatalError("Not implemented", 0, 1);
7536 /* see wgamelist.c for Game List functions */
7537 /* see wedittags.c for Edit Tags functions */
7547 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
7548 f = fopen(buf, "r");
7550 ProcessICSInitScript(f);
7558 StartAnalysisClock()
7560 if (analysisTimerEvent) return;
7561 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
7566 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
7568 static HANDLE hwndText;
7570 static int sizeX, sizeY;
7571 int newSizeX, newSizeY, flags;
7575 case WM_INITDIALOG: /* message: initialize dialog box */
7576 /* Initialize the dialog items */
7577 hwndText = GetDlgItem(hDlg, OPT_AnalysisText);
7578 SetWindowText(hDlg, analysisTitle);
7579 SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);
7580 /* Size and position the dialog */
7581 if (!analysisDialog) {
7582 analysisDialog = hDlg;
7583 flags = SWP_NOZORDER;
7584 GetClientRect(hDlg, &rect);
7586 sizeY = rect.bottom;
7587 if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&
7588 analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {
7590 EnsureOnScreen(&analysisX, &analysisY);
7591 wp.length = sizeof(WINDOWPLACEMENT);
7593 wp.showCmd = SW_SHOW;
7594 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
7595 wp.rcNormalPosition.left = analysisX;
7596 wp.rcNormalPosition.right = analysisX + analysisW;
7597 wp.rcNormalPosition.top = analysisY;
7598 wp.rcNormalPosition.bottom = analysisY + analysisH;
7599 SetWindowPlacement(hDlg, &wp);
7601 GetClientRect(hDlg, &rect);
7602 newSizeX = rect.right;
7603 newSizeY = rect.bottom;
7604 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
7605 newSizeX, newSizeY);
7612 case WM_COMMAND: /* message: received a command */
7613 switch (LOWORD(wParam)) {
7615 /* icsEngineAnalyze */
7616 if (appData.icsActive && appData.icsEngineAnalyze) {
7629 newSizeX = LOWORD(lParam);
7630 newSizeY = HIWORD(lParam);
7631 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
7636 case WM_GETMINMAXINFO:
7637 /* Prevent resizing window too small */
7638 mmi = (MINMAXINFO *) lParam;
7639 mmi->ptMinTrackSize.x = 100;
7640 mmi->ptMinTrackSize.y = 100;
7647 AnalysisPopUp(char* title, char* str)
7652 if (str == NULL) str = "";
7653 p = (char *) malloc(2 * strlen(str) + 2);
7656 if (*str == '\n') *q++ = '\r';
7660 if (analysisText != NULL) free(analysisText);
7663 if (analysisDialog) {
7664 SetWindowText(analysisDialog, title);
7665 SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);
7666 ShowWindow(analysisDialog, SW_SHOW);
7668 analysisTitle = title;
7669 lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);
7670 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),
7671 hwndMain, (DLGPROC)lpProc);
7672 FreeProcInstance(lpProc);
7674 analysisDialogUp = TRUE;
7680 if (analysisDialog) {
7681 ShowWindow(analysisDialog, SW_HIDE);
7683 analysisDialogUp = FALSE;
7688 SetHighlights(int fromX, int fromY, int toX, int toY)
7690 highlightInfo.sq[0].x = fromX;
7691 highlightInfo.sq[0].y = fromY;
7692 highlightInfo.sq[1].x = toX;
7693 highlightInfo.sq[1].y = toY;
7699 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
7700 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
7704 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
7706 premoveHighlightInfo.sq[0].x = fromX;
7707 premoveHighlightInfo.sq[0].y = fromY;
7708 premoveHighlightInfo.sq[1].x = toX;
7709 premoveHighlightInfo.sq[1].y = toY;
7713 ClearPremoveHighlights()
7715 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
7716 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
7722 if (saveSettingsOnExit) SaveSettings(settingsFileName);
7723 DeleteClipboardTempFiles();
7729 if (IsIconic(hwndMain))
7730 ShowWindow(hwndMain, SW_RESTORE);
7732 SetActiveWindow(hwndMain);
7736 * Prototypes for animation support routines
7738 static void ScreenSquare(int column, int row, POINT * pt);
7739 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
7740 POINT frames[], int * nFrames);
7746 AnimateMove(board, fromX, fromY, toX, toY)
7754 POINT start, finish, mid;
7755 POINT frames[kFactor * 2 + 1];
7758 if (!appData.animate) return;
7759 if (doingSizing) return;
7760 if (fromY < 0 || fromX < 0) return;
7761 piece = board[fromY][fromX];
7762 if (piece >= EmptySquare) return;
7764 ScreenSquare(fromX, fromY, &start);
7765 ScreenSquare(toX, toY, &finish);
7767 /* All pieces except knights move in straight line */
7768 if (piece != WhiteKnight && piece != BlackKnight) {
7769 mid.x = start.x + (finish.x - start.x) / 2;
7770 mid.y = start.y + (finish.y - start.y) / 2;
7772 /* Knight: make diagonal movement then straight */
7773 if (abs(toY - fromY) < abs(toX - fromX)) {
7774 mid.x = start.x + (finish.x - start.x) / 2;
7778 mid.y = start.y + (finish.y - start.y) / 2;
7782 /* Don't use as many frames for very short moves */
7783 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7784 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7786 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7788 animInfo.from.x = fromX;
7789 animInfo.from.y = fromY;
7790 animInfo.to.x = toX;
7791 animInfo.to.y = toY;
7792 animInfo.lastpos = start;
7793 animInfo.piece = piece;
7794 for (n = 0; n < nFrames; n++) {
7795 animInfo.pos = frames[n];
7796 DrawPosition(FALSE, NULL);
7797 animInfo.lastpos = animInfo.pos;
7798 Sleep(appData.animSpeed);
7800 animInfo.pos = finish;
7801 DrawPosition(FALSE, NULL);
7802 animInfo.piece = EmptySquare;
7805 /* Convert board position to corner of screen rect and color */
7808 ScreenSquare(column, row, pt)
7809 int column; int row; POINT * pt;
7812 pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
7813 pt->y = lineGap + row * (squareSize + lineGap);
7815 pt->x = lineGap + column * (squareSize + lineGap);
7816 pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
7820 /* Generate a series of frame coords from start->mid->finish.
7821 The movement rate doubles until the half way point is
7822 reached, then halves back down to the final destination,
7823 which gives a nice slow in/out effect. The algorithmn
7824 may seem to generate too many intermediates for short
7825 moves, but remember that the purpose is to attract the
7826 viewers attention to the piece about to be moved and
7827 then to where it ends up. Too few frames would be less
7831 Tween(start, mid, finish, factor, frames, nFrames)
7832 POINT * start; POINT * mid;
7833 POINT * finish; int factor;
7834 POINT frames[]; int * nFrames;
7836 int n, fraction = 1, count = 0;
7838 /* Slow in, stepping 1/16th, then 1/8th, ... */
7839 for (n = 0; n < factor; n++)
7841 for (n = 0; n < factor; n++) {
7842 frames[count].x = start->x + (mid->x - start->x) / fraction;
7843 frames[count].y = start->y + (mid->y - start->y) / fraction;
7845 fraction = fraction / 2;
7849 frames[count] = *mid;
7852 /* Slow out, stepping 1/2, then 1/4, ... */
7854 for (n = 0; n < factor; n++) {
7855 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
7856 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
7858 fraction = fraction * 2;
7864 HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
7866 /* Currently not implemented in WinBoard */