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");
3655 appData.icsEngineAnalyze = FALSE;
3662 /* if enable, user want disable icsEngineAnalyze */
3663 if (appData.icsEngineAnalyze) {
3664 appData.icsEngineAnalyze = FALSE;
3669 appData.icsEngineAnalyze = TRUE;
3670 if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");
3673 if (!appData.showThinking) ToggleShowThinking();
3677 case IDM_AnalyzeFile:
3678 if (!first.analysisSupport) {
3680 sprintf(buf, "%s does not support analysis", first.tidy);
3681 DisplayError(buf, 0);
3683 if (!appData.showThinking) ToggleShowThinking();
3685 LoadGameDialog(hwnd, "Analyze Game from File");
3686 AnalysisPeriodicEvent(1);
3698 case IDM_EditPosition:
3699 EditPositionEvent();
3706 case IDM_ShowGameList:
3714 case IDM_EditComment:
3715 if (commentDialogUp && editComment) {
3758 case IDM_StopObserving:
3759 StopObservingEvent();
3762 case IDM_StopExamining:
3763 StopExaminingEvent();
3766 case IDM_TypeInMove:
3767 PopUpMoveDialog('\000');
3794 case IDM_TruncateGame:
3795 TruncateGameEvent();
3802 case IDM_RetractMove:
3807 flipView = !flipView;
3808 DrawPosition(FALSE, NULL);
3811 case IDM_GeneralOptions:
3812 GeneralOptionsPopup(hwnd);
3815 case IDM_BoardOptions:
3816 BoardOptionsPopup(hwnd);
3819 case IDM_IcsOptions:
3820 IcsOptionsPopup(hwnd);
3824 FontsOptionsPopup(hwnd);
3828 SoundOptionsPopup(hwnd);
3832 CommPortOptionsPopup(hwnd);
3835 case IDM_LoadOptions:
3836 LoadOptionsPopup(hwnd);
3839 case IDM_SaveOptions:
3840 SaveOptionsPopup(hwnd);
3843 case IDM_TimeControl:
3844 TimeControlOptionsPopup(hwnd);
3847 case IDM_SaveSettings:
3848 SaveSettings(settingsFileName);
3851 case IDM_SaveSettingsOnExit:
3852 saveSettingsOnExit = !saveSettingsOnExit;
3853 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
3854 MF_BYCOMMAND|(saveSettingsOnExit ?
3855 MF_CHECKED : MF_UNCHECKED));
3871 appData.debugMode = !appData.debugMode;
3872 if (appData.debugMode) {
3874 GetCurrentDirectory(MSG_SIZ, dir);
3875 SetCurrentDirectory(installDir);
3876 debugFP = fopen("WinBoard.debug", "w");
3877 SetCurrentDirectory(dir);
3878 setbuf(debugFP, NULL);
3885 case IDM_HELPCONTENTS:
3886 if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
3887 MessageBox (GetFocus(),
3888 "Unable to activate help",
3889 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3893 case IDM_HELPSEARCH:
3894 if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
3895 MessageBox (GetFocus(),
3896 "Unable to activate help",
3897 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3902 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
3903 MessageBox (GetFocus(),
3904 "Unable to activate help",
3905 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3910 lpProc = MakeProcInstance((FARPROC)About, hInst);
3912 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
3913 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
3914 FreeProcInstance(lpProc);
3917 case IDM_DirectCommand1:
3918 AskQuestionEvent("Direct Command",
3919 "Send to chess program:", "", "1");
3921 case IDM_DirectCommand2:
3922 AskQuestionEvent("Direct Command",
3923 "Send to second chess program:", "", "2");
3927 EditPositionMenuEvent(WhitePawn, fromX, fromY);
3931 case EP_WhiteKnight:
3932 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
3936 case EP_WhiteBishop:
3937 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
3942 EditPositionMenuEvent(WhiteRook, fromX, fromY);
3947 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
3952 EditPositionMenuEvent(WhiteKing, fromX, fromY);
3957 EditPositionMenuEvent(BlackPawn, fromX, fromY);
3961 case EP_BlackKnight:
3962 EditPositionMenuEvent(BlackKnight, fromX, fromY);
3966 case EP_BlackBishop:
3967 EditPositionMenuEvent(BlackBishop, fromX, fromY);
3972 EditPositionMenuEvent(BlackRook, fromX, fromY);
3977 EditPositionMenuEvent(BlackQueen, fromX, fromY);
3982 EditPositionMenuEvent(BlackKing, fromX, fromY);
3986 case EP_EmptySquare:
3987 EditPositionMenuEvent(EmptySquare, fromX, fromY);
3992 EditPositionMenuEvent(ClearBoard, fromX, fromY);
3997 EditPositionMenuEvent(WhitePlay, fromX, fromY);
4002 EditPositionMenuEvent(BlackPlay, fromX, fromY);
4007 DropMenuEvent(WhitePawn, fromX, fromY);
4012 DropMenuEvent(WhiteKnight, fromX, fromY);
4017 DropMenuEvent(WhiteBishop, fromX, fromY);
4022 DropMenuEvent(WhiteRook, fromX, fromY);
4027 DropMenuEvent(WhiteQueen, fromX, fromY);
4032 return (DefWindowProc(hwnd, message, wParam, lParam));
4038 case CLOCK_TIMER_ID:
4039 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
4040 clockTimerEvent = 0;
4041 DecrementClocks(); /* call into back end */
4043 case LOAD_GAME_TIMER_ID:
4044 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
4045 loadGameTimerEvent = 0;
4046 AutoPlayGameLoop(); /* call into back end */
4048 case ANALYSIS_TIMER_ID:
4049 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
4050 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
4051 AnalysisPeriodicEvent(0);
4053 KillTimer(hwnd, analysisTimerEvent);
4054 analysisTimerEvent = 0;
4057 case DELAYED_TIMER_ID:
4058 KillTimer(hwnd, delayedTimerEvent);
4059 delayedTimerEvent = 0;
4060 delayedTimerCallback();
4066 InputEvent(hwnd, message, wParam, lParam);
4069 case WM_ENTERSIZEMOVE:
4070 if (hwnd == hwndMain) {
4077 if (hwnd == hwndMain) {
4078 lastSizing = wParam;
4082 case WM_EXITSIZEMOVE:
4083 if (hwnd == hwndMain) {
4085 doingSizing = FALSE;
4086 InvalidateRect(hwnd, &boardRect, FALSE);
4087 GetClientRect(hwnd, &client);
4088 ResizeBoard(client.right, client.bottom, lastSizing);
4093 case WM_DESTROY: /* message: window being destroyed */
4098 if (hwnd == hwndMain) {
4103 default: /* Passes it on if unprocessed */
4104 return (DefWindowProc(hwnd, message, wParam, lParam));
4109 /*---------------------------------------------------------------------------*\
4111 * Misc utility routines
4113 \*---------------------------------------------------------------------------*/
4116 * Decent random number generator, at least not as bad as Windows
4117 * standard rand, which returns a value in the range 0 to 0x7fff.
4119 unsigned int randstate;
4124 randstate = randstate * 1664525 + 1013904223;
4125 return (int) randstate & 0x7fffffff;
4129 mysrandom(unsigned int seed)
4136 * returns TRUE if user selects a different color, FALSE otherwise
4140 ChangeColor(HWND hwnd, COLORREF *which)
4142 static BOOL firstTime = TRUE;
4143 static DWORD customColors[16];
4150 /* Make initial colors in use available as custom colors */
4151 /* Should we put the compiled-in defaults here instead? */
4153 customColors[i++] = lightSquareColor & 0xffffff;
4154 customColors[i++] = darkSquareColor & 0xffffff;
4155 customColors[i++] = whitePieceColor & 0xffffff;
4156 customColors[i++] = blackPieceColor & 0xffffff;
4157 customColors[i++] = highlightSquareColor & 0xffffff;
4158 customColors[i++] = premoveHighlightColor & 0xffffff;
4160 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
4161 customColors[i++] = textAttribs[ccl].color;
4163 while (i < 16) customColors[i++] = RGB(255, 255, 255);
4167 cc.lStructSize = sizeof(cc);
4168 cc.hwndOwner = hwnd;
4169 cc.hInstance = NULL;
4170 cc.rgbResult = (DWORD) (*which & 0xffffff);
4171 cc.lpCustColors = (LPDWORD) customColors;
4172 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
4174 if (!ChooseColor(&cc)) return FALSE;
4176 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
4177 if (newcolor == *which) return FALSE;
4182 InitDrawingColors();
4183 InvalidateRect(hwnd, &boardRect, FALSE);
4188 MyLoadSound(MySound *ms)
4194 if (ms->data) free(ms->data);
4197 switch (ms->name[0]) {
4203 /* System sound from Control Panel. Don't preload here. */
4207 if (ms->name[1] == NULLCHAR) {
4208 /* "!" alone = silence */
4211 /* Builtin wave resource. Error if not found. */
4212 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
4213 if (h == NULL) break;
4214 ms->data = (void *)LoadResource(hInst, h);
4215 if (h == NULL) break;
4220 /* .wav file. Error if not found. */
4221 f = fopen(ms->name, "rb");
4222 if (f == NULL) break;
4223 if (fstat(fileno(f), &st) < 0) break;
4224 ms->data = malloc(st.st_size);
4225 if (fread(ms->data, st.st_size, 1, f) < 1) break;
4232 sprintf(buf, "Error loading sound %s", ms->name);
4233 DisplayError(buf, GetLastError());
4239 MyPlaySound(MySound *ms)
4242 switch (ms->name[0]) {
4248 /* System sound from Control Panel (deprecated feature).
4249 "$" alone or an unset sound name gets default beep (still in use). */
4251 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
4253 if (!ok) ok = MessageBeep(MB_OK);
4256 /* Builtin wave resource, or "!" alone for silence */
4258 if (ms->data == NULL) return FALSE;
4259 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4265 /* .wav file. Error if not found. */
4266 if (ms->data == NULL) return FALSE;
4267 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4270 /* Don't print an error: this can happen innocently if the sound driver
4271 is busy; for instance, if another instance of WinBoard is playing
4272 a sound at about the same time. */
4276 sprintf(buf, "Error playing sound %s", ms->name);
4277 DisplayError(buf, GetLastError());
4285 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4289 static UINT *number; /* gross that this is static */
4292 case WM_INITDIALOG: /* message: initialize dialog box */
4293 /* Center the dialog over the application window */
4294 ofn = (OPENFILENAME *) lParam;
4295 if (ofn->Flags & OFN_ENABLETEMPLATE) {
4296 number = (UINT *) ofn->lCustData;
4297 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
4301 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4302 return FALSE; /* Allow for further processing */
4305 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
4306 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
4308 return FALSE; /* Allow for further processing */
4314 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
4316 static UINT *number;
4317 OPENFILENAME *ofname;
4321 ofname = (OPENFILENAME *)lParam;
4322 number = (UINT *)(ofname->lCustData);
4325 ofnot = (OFNOTIFY *)lParam;
4326 if (ofnot->hdr.code == CDN_FILEOK) {
4327 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
4336 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,
4337 char *nameFilt, char *dlgTitle, UINT *number,
4338 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
4340 OPENFILENAME openFileName;
4344 if (fileName == NULL) fileName = buf1;
4345 if (defName == NULL) {
4346 strcpy(fileName, "*.");
4347 strcat(fileName, defExt);
4349 strcpy(fileName, defName);
4351 if (fileTitle) strcpy(fileTitle, "");
4352 if (number) *number = 0;
4354 openFileName.lStructSize = sizeof(OPENFILENAME);
4355 openFileName.hwndOwner = hwnd;
4356 openFileName.hInstance = (HANDLE) hInst;
4357 openFileName.lpstrFilter = nameFilt;
4358 openFileName.lpstrCustomFilter = (LPSTR) NULL;
4359 openFileName.nMaxCustFilter = 0L;
4360 openFileName.nFilterIndex = 1L;
4361 openFileName.lpstrFile = fileName;
4362 openFileName.nMaxFile = MSG_SIZ;
4363 openFileName.lpstrFileTitle = fileTitle;
4364 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
4365 openFileName.lpstrInitialDir = NULL;
4366 openFileName.lpstrTitle = dlgTitle;
4367 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
4368 | (write ? 0 : OFN_FILEMUSTEXIST)
4369 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
4370 | (oldDialog ? 0 : OFN_EXPLORER);
4371 openFileName.nFileOffset = 0;
4372 openFileName.nFileExtension = 0;
4373 openFileName.lpstrDefExt = defExt;
4374 openFileName.lCustData = (LONG) number;
4375 openFileName.lpfnHook = oldDialog ?
4376 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
4377 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
4379 if (write ? GetSaveFileName(&openFileName) :
4380 GetOpenFileName(&openFileName)) {
4382 f = fopen(openFileName.lpstrFile, write ? "a" : "rb");
4384 MessageBox(hwnd, "File open failed", NULL,
4385 MB_OK|MB_ICONEXCLAMATION);
4389 int err = CommDlgExtendedError();
4390 if (err != 0) DisplayError("Internal error in file dialog box", err);
4399 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
4401 HMENU hmenuTrackPopup; /* floating pop-up menu */
4404 * Get the first pop-up menu in the menu template. This is the
4405 * menu that TrackPopupMenu displays.
4407 hmenuTrackPopup = GetSubMenu(hmenu, 0);
4409 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
4412 * TrackPopup uses screen coordinates, so convert the
4413 * coordinates of the mouse click to screen coordinates.
4415 ClientToScreen(hwnd, (LPPOINT) &pt);
4417 /* Draw and track the floating pop-up menu. */
4418 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
4419 pt.x, pt.y, 0, hwnd, NULL);
4421 /* Destroy the menu.*/
4427 int sizeX, sizeY, newSizeX, newSizeY;
4429 } ResizeEditPlusButtonsClosure;
4432 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
4434 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
4438 if (hChild == cl->hText) return TRUE;
4439 GetWindowRect(hChild, &rect); /* gives screen coords */
4440 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
4441 pt.y = rect.top + cl->newSizeY - cl->sizeY;
4442 ScreenToClient(cl->hDlg, &pt);
4443 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
4444 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
4448 /* Resize a dialog that has a (rich) edit field filling most of
4449 the top, with a row of buttons below */
4451 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
4454 int newTextHeight, newTextWidth;
4455 ResizeEditPlusButtonsClosure cl;
4457 /*if (IsIconic(hDlg)) return;*/
4458 if (newSizeX == sizeX && newSizeY == sizeY) return;
4460 cl.hdwp = BeginDeferWindowPos(8);
4462 GetWindowRect(hText, &rectText); /* gives screen coords */
4463 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
4464 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
4465 if (newTextHeight < 0) {
4466 newSizeY += -newTextHeight;
4469 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
4470 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
4476 cl.newSizeX = newSizeX;
4477 cl.newSizeY = newSizeY;
4478 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
4480 EndDeferWindowPos(cl.hdwp);
4483 /* Center one window over another */
4484 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
4486 RECT rChild, rParent;
4487 int wChild, hChild, wParent, hParent;
4488 int wScreen, hScreen, xNew, yNew;
4491 /* Get the Height and Width of the child window */
4492 GetWindowRect (hwndChild, &rChild);
4493 wChild = rChild.right - rChild.left;
4494 hChild = rChild.bottom - rChild.top;
4496 /* Get the Height and Width of the parent window */
4497 GetWindowRect (hwndParent, &rParent);
4498 wParent = rParent.right - rParent.left;
4499 hParent = rParent.bottom - rParent.top;
4501 /* Get the display limits */
4502 hdc = GetDC (hwndChild);
4503 wScreen = GetDeviceCaps (hdc, HORZRES);
4504 hScreen = GetDeviceCaps (hdc, VERTRES);
4505 ReleaseDC(hwndChild, hdc);
4507 /* Calculate new X position, then adjust for screen */
4508 xNew = rParent.left + ((wParent - wChild) /2);
4511 } else if ((xNew+wChild) > wScreen) {
4512 xNew = wScreen - wChild;
4515 /* Calculate new Y position, then adjust for screen */
4516 yNew = rParent.top + ((hParent - hChild) /2);
4519 } else if ((yNew+hChild) > hScreen) {
4520 yNew = hScreen - hChild;
4523 /* Set it, and return */
4524 return SetWindowPos (hwndChild, NULL,
4525 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4528 /*---------------------------------------------------------------------------*\
4530 * Startup Dialog functions
4532 \*---------------------------------------------------------------------------*/
4534 InitComboStrings(HANDLE hwndCombo, char **cd)
4536 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4538 while (*cd != NULL) {
4539 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
4545 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
4550 if (str[0] == '@') {
4551 FILE* f = fopen(str + 1, "r");
4553 DisplayFatalError(str + 1, errno, 2);
4556 len = fread(buf1, 1, sizeof(buf1)-1, f);
4558 buf1[len] = NULLCHAR;
4562 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4566 char *end = strchr(str, '\n');
4567 if (end == NULL) return;
4568 memcpy(buf, str, end - str);
4569 buf[end - str] = NULLCHAR;
4570 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
4576 SetStartupDialogEnables(HWND hDlg)
4578 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
4579 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4580 appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));
4581 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
4582 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
4583 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
4584 IsDlgButtonChecked(hDlg, OPT_ChessServer));
4585 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
4586 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
4587 EnableWindow(GetDlgItem(hDlg, IDOK),
4588 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4589 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
4590 IsDlgButtonChecked(hDlg, OPT_View));
4594 QuoteForFilename(char *filename)
4597 dquote = strchr(filename, '"') != NULL;
4598 space = strchr(filename, ' ') != NULL;
4599 if (dquote || space) {
4611 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
4616 InitComboStringsFromOption(hwndCombo, nthnames);
4617 q = QuoteForFilename(nthcp);
4618 sprintf(buf, "%s%s%s", q, nthcp, q);
4619 if (*nthdir != NULLCHAR) {
4620 q = QuoteForFilename(nthdir);
4621 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
4623 if (*nthcp == NULLCHAR) {
4624 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4625 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4626 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4627 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4632 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4640 /* Center the dialog */
4641 CenterWindow (hDlg, GetDesktopWindow());
4642 /* Initialize the dialog items */
4643 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
4644 appData.firstChessProgram, "fd", appData.firstDirectory,
4645 firstChessProgramNames);
4646 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
4647 appData.secondChessProgram, "sd", appData.secondDirectory,
4648 secondChessProgramNames);
4649 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
4650 InitComboStringsFromOption(hwndCombo, icsNames);
4651 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
4652 if (*appData.icsHelper != NULLCHAR) {
4653 char *q = QuoteForFilename(appData.icsHelper);
4654 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
4656 if (*appData.icsHost == NULLCHAR) {
4657 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4658 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
4659 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4660 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4661 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4664 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
4665 } else if (appData.icsActive) {
4666 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
4667 } else if (appData.noChessProgram) {
4668 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
4670 SetStartupDialogEnables(hDlg);
4674 switch (LOWORD(wParam)) {
4676 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
4677 strcpy(buf, "/fcp=");
4678 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4680 ParseArgs(StringGet, &p);
4681 strcpy(buf, "/scp=");
4682 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4684 ParseArgs(StringGet, &p);
4685 appData.noChessProgram = FALSE;
4686 appData.icsActive = FALSE;
4687 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
4688 strcpy(buf, "/ics /icshost=");
4689 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4691 ParseArgs(StringGet, &p);
4692 if (appData.zippyPlay) {
4693 strcpy(buf, "/fcp=");
4694 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4696 ParseArgs(StringGet, &p);
4698 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
4699 appData.noChessProgram = TRUE;
4700 appData.icsActive = FALSE;
4702 MessageBox(hDlg, "Choose an option, or cancel to exit",
4703 "Option Error", MB_OK|MB_ICONEXCLAMATION);
4706 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
4707 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
4709 ParseArgs(StringGet, &p);
4711 EndDialog(hDlg, TRUE);
4718 case IDM_HELPCONTENTS:
4719 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
4720 MessageBox (GetFocus(),
4721 "Unable to activate help",
4722 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
4727 SetStartupDialogEnables(hDlg);
4735 /*---------------------------------------------------------------------------*\
4737 * About box dialog functions
4739 \*---------------------------------------------------------------------------*/
4741 /* Process messages for "About" dialog box */
4743 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4746 case WM_INITDIALOG: /* message: initialize dialog box */
4747 /* Center the dialog over the application window */
4748 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4749 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
4752 case WM_COMMAND: /* message: received a command */
4753 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
4754 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
4755 EndDialog(hDlg, TRUE); /* Exit the dialog */
4763 /*---------------------------------------------------------------------------*\
4765 * Comment Dialog functions
4767 \*---------------------------------------------------------------------------*/
4770 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4772 static HANDLE hwndText = NULL;
4773 int len, newSizeX, newSizeY, flags;
4774 static int sizeX, sizeY;
4780 case WM_INITDIALOG: /* message: initialize dialog box */
4781 /* Initialize the dialog items */
4782 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4783 SetDlgItemText(hDlg, OPT_CommentText, commentText);
4784 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
4785 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
4786 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
4787 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
4788 SetWindowText(hDlg, commentTitle);
4792 SetFocus(GetDlgItem(hDlg, IDOK));
4794 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
4795 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
4796 MAKELPARAM(FALSE, 0));
4797 /* Size and position the dialog */
4798 if (!commentDialog) {
4799 commentDialog = hDlg;
4800 flags = SWP_NOZORDER;
4801 GetClientRect(hDlg, &rect);
4803 sizeY = rect.bottom;
4804 if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&
4805 commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {
4807 EnsureOnScreen(&commentX, &commentY);
4808 wp.length = sizeof(WINDOWPLACEMENT);
4810 wp.showCmd = SW_SHOW;
4811 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
4812 wp.rcNormalPosition.left = commentX;
4813 wp.rcNormalPosition.right = commentX + commentW;
4814 wp.rcNormalPosition.top = commentY;
4815 wp.rcNormalPosition.bottom = commentY + commentH;
4816 SetWindowPlacement(hDlg, &wp);
4818 GetClientRect(hDlg, &rect);
4819 newSizeX = rect.right;
4820 newSizeY = rect.bottom;
4821 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
4822 newSizeX, newSizeY);
4829 case WM_COMMAND: /* message: received a command */
4830 switch (LOWORD(wParam)) {
4834 /* Read changed options from the dialog box */
4835 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4836 len = GetWindowTextLength(hwndText);
4837 str = (char *) malloc(len + 1);
4838 GetWindowText(hwndText, str, len + 1);
4847 ReplaceComment(commentIndex, str);
4854 case OPT_CancelComment:
4858 case OPT_ClearComment:
4859 SetDlgItemText(hDlg, OPT_CommentText, "");
4862 case OPT_EditComment:
4872 newSizeX = LOWORD(lParam);
4873 newSizeY = HIWORD(lParam);
4874 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
4879 case WM_GETMINMAXINFO:
4880 /* Prevent resizing window too small */
4881 mmi = (MINMAXINFO *) lParam;
4882 mmi->ptMinTrackSize.x = 100;
4883 mmi->ptMinTrackSize.y = 100;
4890 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
4895 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
4897 if (str == NULL) str = "";
4898 p = (char *) malloc(2 * strlen(str) + 2);
4901 if (*str == '\n') *q++ = '\r';
4905 if (commentText != NULL) free(commentText);
4907 commentIndex = index;
4908 commentTitle = title;
4912 if (commentDialog) {
4913 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
4914 if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);
4916 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
4917 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
4918 hwndMain, (DLGPROC)lpProc);
4919 FreeProcInstance(lpProc);
4921 commentDialogUp = TRUE;
4925 /*---------------------------------------------------------------------------*\
4927 * Type-in move dialog functions
4929 \*---------------------------------------------------------------------------*/
4932 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4937 int fromX, fromY, toX, toY;
4942 move[0] = (char) lParam;
4944 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
4945 hInput = GetDlgItem(hDlg, OPT_Move);
4946 SetWindowText(hInput, move);
4948 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
4952 switch (LOWORD(wParam)) {
4954 if (gameMode != EditGame && currentMove != forwardMostMove &&
4955 gameMode != Training) {
4956 DisplayMoveError("Displayed move is not current");
4958 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
4959 if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
4960 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
4961 if (gameMode != Training)
4962 forwardMostMove = currentMove;
4963 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4965 DisplayMoveError("Could not parse move");
4968 EndDialog(hDlg, TRUE);
4971 EndDialog(hDlg, FALSE);
4982 PopUpMoveDialog(char firstchar)
4986 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
4987 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
4988 gameMode == AnalyzeMode || gameMode == EditGame ||
4989 gameMode == EditPosition || gameMode == IcsExamining ||
4990 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
4991 gameMode == Training) {
4992 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
4993 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
4994 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
4995 FreeProcInstance(lpProc);
4999 /*---------------------------------------------------------------------------*\
5003 \*---------------------------------------------------------------------------*/
5005 /* Nonmodal error box */
5006 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
5007 WPARAM wParam, LPARAM lParam);
5010 ErrorPopUp(char *title, char *content)
5014 BOOLEAN modal = hwndMain == NULL;
5032 strncpy(errorTitle, title, sizeof(errorTitle));
5033 errorTitle[sizeof(errorTitle) - 1] = '\0';
5036 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
5038 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
5039 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
5040 hwndMain, (DLGPROC)lpProc);
5041 FreeProcInstance(lpProc);
5048 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
5049 if (errorDialog == NULL) return;
5050 DestroyWindow(errorDialog);
5055 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5062 GetWindowRect(hDlg, &rChild);
5063 SetWindowPos(hDlg, NULL, rChild.left,
5064 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
5065 0, 0, SWP_NOZORDER|SWP_NOSIZE);
5067 SetWindowText(hDlg, errorTitle);
5068 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
5069 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
5073 switch (LOWORD(wParam)) {
5076 if (errorDialog == hDlg) errorDialog = NULL;
5077 DestroyWindow(hDlg);
5088 /*---------------------------------------------------------------------------*\
5090 * Ics Interaction console functions
5092 \*---------------------------------------------------------------------------*/
5094 #define HISTORY_SIZE 64
5095 static char *history[HISTORY_SIZE];
5096 int histIn = 0, histP = 0;
5099 SaveInHistory(char *cmd)
5101 if (history[histIn] != NULL) {
5102 free(history[histIn]);
5103 history[histIn] = NULL;
5105 if (*cmd == NULLCHAR) return;
5106 history[histIn] = StrSave(cmd);
5107 histIn = (histIn + 1) % HISTORY_SIZE;
5108 if (history[histIn] != NULL) {
5109 free(history[histIn]);
5110 history[histIn] = NULL;
5116 PrevInHistory(char *cmd)
5119 if (histP == histIn) {
5120 if (history[histIn] != NULL) free(history[histIn]);
5121 history[histIn] = StrSave(cmd);
5123 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
5124 if (newhp == histIn || history[newhp] == NULL) return NULL;
5126 return history[histP];
5132 if (histP == histIn) return NULL;
5133 histP = (histP + 1) % HISTORY_SIZE;
5134 return history[histP];
5143 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
5144 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];
5147 ParseIcsTextMenu(char *icsTextMenuString)
5150 IcsTextMenuEntry *e = icsTextMenuEntry;
5151 char *p = icsTextMenuString;
5152 while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5155 if (e->command != NULL) {
5161 e = icsTextMenuEntry;
5162 while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5163 if (*p == ';' || *p == '\n') {
5164 e->item = strdup("-");
5167 } else if (*p == '-') {
5168 e->item = strdup("-");
5173 char *q, *r, *s, *t;
5176 if (q == NULL) break;
5178 r = strchr(q + 1, ',');
5179 if (r == NULL) break;
5181 s = strchr(r + 1, ',');
5182 if (s == NULL) break;
5185 t = strchr(s + 1, c);
5188 t = strchr(s + 1, c);
5190 if (t != NULL) *t = NULLCHAR;
5191 e->item = strdup(p);
5192 e->command = strdup(q + 1);
5193 e->getname = *(r + 1) != '0';
5194 e->immediate = *(s + 1) != '0';
5198 if (t == NULL) break;
5207 LoadIcsTextMenu(IcsTextMenuEntry *e)
5211 hmenu = LoadMenu(hInst, "TextMenu");
5212 h = GetSubMenu(hmenu, 0);
5214 if (strcmp(e->item, "-") == 0) {
5215 AppendMenu(h, MF_SEPARATOR, 0, 0);
5217 if (e->item[0] == '|') {
5218 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
5219 IDM_CommandX + i, &e->item[1]);
5221 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
5230 WNDPROC consoleTextWindowProc;
5233 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
5235 char buf[MSG_SIZ], name[MSG_SIZ];
5236 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5240 SetWindowText(hInput, command);
5242 SendMessage(hInput, WM_CHAR, '\r', 0);
5246 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5251 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5252 if (sel.cpMin == sel.cpMax) {
5253 /* Expand to surrounding word */
5256 tr.chrg.cpMax = sel.cpMin;
5257 tr.chrg.cpMin = --sel.cpMin;
5258 if (sel.cpMin < 0) break;
5259 tr.lpstrText = name;
5260 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5261 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5265 tr.chrg.cpMin = sel.cpMax;
5266 tr.chrg.cpMax = ++sel.cpMax;
5267 tr.lpstrText = name;
5268 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
5269 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5272 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5273 MessageBeep(MB_ICONEXCLAMATION);
5277 tr.lpstrText = name;
5278 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5280 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5281 MessageBeep(MB_ICONEXCLAMATION);
5284 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
5287 sprintf(buf, "%s %s", command, name);
5288 SetWindowText(hInput, buf);
5289 SendMessage(hInput, WM_CHAR, '\r', 0);
5291 sprintf(buf, "%s %s ", command, name); /* trailing space */
5292 SetWindowText(hInput, buf);
5295 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5301 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5308 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5311 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
5316 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5317 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
5322 if (wParam == '\t') {
5323 if (GetKeyState(VK_SHIFT) < 0) {
5325 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5326 if (buttonDesc[0].hwnd) {
5327 SetFocus(buttonDesc[0].hwnd);
5333 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
5336 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5338 SendMessage(hInput, message, wParam, lParam);
5342 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5344 return SendMessage(hInput, message, wParam, lParam);
5345 case WM_MBUTTONDOWN:
5346 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5347 case WM_RBUTTONDOWN:
5348 if (!(GetKeyState(VK_SHIFT) & ~1)) {
5349 /* Move selection here if it was empty */
5351 pt.x = LOWORD(lParam);
5352 pt.y = HIWORD(lParam);
5353 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5354 if (sel.cpMin == sel.cpMax) {
5355 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
5356 sel.cpMax = sel.cpMin;
5357 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5359 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
5363 if (GetKeyState(VK_SHIFT) & ~1) {
5364 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5365 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5368 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
5369 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5370 if (sel.cpMin == sel.cpMax) {
5371 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5372 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
5374 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5375 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5377 pt.x = LOWORD(lParam);
5378 pt.y = HIWORD(lParam);
5379 MenuPopup(hwnd, pt, hmenu, -1);
5383 switch (LOWORD(wParam)) {
5384 case IDM_QuickPaste:
5386 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5387 if (sel.cpMin == sel.cpMax) {
5388 MessageBeep(MB_ICONEXCLAMATION);
5391 SendMessage(hwnd, WM_COPY, 0, 0);
5392 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5393 SendMessage(hInput, WM_PASTE, 0, 0);
5398 SendMessage(hwnd, WM_CUT, 0, 0);
5401 SendMessage(hwnd, WM_PASTE, 0, 0);
5404 SendMessage(hwnd, WM_COPY, 0, 0);
5408 int i = LOWORD(wParam) - IDM_CommandX;
5409 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
5410 icsTextMenuEntry[i].command != NULL) {
5411 CommandX(hwnd, icsTextMenuEntry[i].command,
5412 icsTextMenuEntry[i].getname,
5413 icsTextMenuEntry[i].immediate);
5421 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
5424 WNDPROC consoleInputWindowProc;
5427 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5431 static BOOL sendNextChar = FALSE;
5432 static BOOL quoteNextChar = FALSE;
5433 InputSource *is = consoleInputSource;
5439 if (!appData.localLineEditing || sendNextChar) {
5440 is->buf[0] = (CHAR) wParam;
5442 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5443 sendNextChar = FALSE;
5446 if (quoteNextChar) {
5447 buf[0] = (char) wParam;
5449 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
5450 quoteNextChar = FALSE;
5454 case '\r': /* Enter key */
5455 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
5456 if (consoleEcho) SaveInHistory(is->buf);
5457 is->buf[is->count++] = '\n';
5458 is->buf[is->count] = NULLCHAR;
5459 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5461 ConsoleOutput(is->buf, is->count, TRUE);
5462 } else if (appData.localLineEditing) {
5463 ConsoleOutput("\n", 1, TRUE);
5466 case '\033': /* Escape key */
5467 SetWindowText(hwnd, "");
5468 cf.cbSize = sizeof(CHARFORMAT);
5469 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
5471 cf.crTextColor = textAttribs[ColorNormal].color;
5473 cf.crTextColor = COLOR_ECHOOFF;
5475 cf.dwEffects = textAttribs[ColorNormal].effects;
5476 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
5478 case '\t': /* Tab key */
5479 if (GetKeyState(VK_SHIFT) < 0) {
5481 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
5484 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5485 if (buttonDesc[0].hwnd) {
5486 SetFocus(buttonDesc[0].hwnd);
5492 case '\023': /* Ctrl+S */
5493 sendNextChar = TRUE;
5495 case '\021': /* Ctrl+Q */
5496 quoteNextChar = TRUE;
5505 GetWindowText(hwnd, buf, MSG_SIZ);
5506 p = PrevInHistory(buf);
5508 SetWindowText(hwnd, p);
5511 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5516 p = NextInHistory();
5518 SetWindowText(hwnd, p);
5521 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5527 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5531 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
5535 case WM_MBUTTONDOWN:
5536 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5537 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5540 if (GetKeyState(VK_SHIFT) & ~1) {
5541 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5542 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5546 hmenu = LoadMenu(hInst, "InputMenu");
5547 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5548 if (sel.cpMin == sel.cpMax) {
5549 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5550 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
5552 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5553 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5555 pt.x = LOWORD(lParam);
5556 pt.y = HIWORD(lParam);
5557 MenuPopup(hwnd, pt, hmenu, -1);
5561 switch (LOWORD(wParam)) {
5563 SendMessage(hwnd, EM_UNDO, 0, 0);
5567 sel.cpMax = -1; /*999999?*/
5568 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5571 SendMessage(hwnd, WM_CUT, 0, 0);
5574 SendMessage(hwnd, WM_PASTE, 0, 0);
5577 SendMessage(hwnd, WM_COPY, 0, 0);
5582 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
5585 #define CO_MAX 100000
5586 #define CO_TRIM 1000
5589 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5591 static HWND hText, hInput, hFocus;
5592 InputSource *is = consoleInputSource;
5594 static int sizeX, sizeY;
5595 int newSizeX, newSizeY;
5599 case WM_INITDIALOG: /* message: initialize dialog box */
5601 hText = GetDlgItem(hDlg, OPT_ConsoleText);
5602 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
5604 consoleTextWindowProc = (WNDPROC)
5605 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
5606 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5607 consoleInputWindowProc = (WNDPROC)
5608 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
5609 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5610 Colorize(ColorNormal, TRUE);
5611 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
5612 ChangedConsoleFont();
5613 GetClientRect(hDlg, &rect);
5615 sizeY = rect.bottom;
5616 if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&
5617 consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {
5619 EnsureOnScreen(&consoleX, &consoleY);
5620 wp.length = sizeof(WINDOWPLACEMENT);
5622 wp.showCmd = SW_SHOW;
5623 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
5624 wp.rcNormalPosition.left = consoleX;
5625 wp.rcNormalPosition.right = consoleX + consoleW;
5626 wp.rcNormalPosition.top = consoleY;
5627 wp.rcNormalPosition.bottom = consoleY + consoleH;
5628 SetWindowPlacement(hDlg, &wp);
5642 if (IsIconic(hDlg)) break;
5643 newSizeX = LOWORD(lParam);
5644 newSizeY = HIWORD(lParam);
5645 if (sizeX != newSizeX || sizeY != newSizeY) {
5646 RECT rectText, rectInput;
5648 int newTextHeight, newTextWidth;
5649 GetWindowRect(hText, &rectText);
5650 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
5651 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
5652 if (newTextHeight < 0) {
5653 newSizeY += -newTextHeight;
5656 SetWindowPos(hText, NULL, 0, 0,
5657 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
5658 GetWindowRect(hInput, &rectInput); /* gives screen coords */
5659 pt.x = rectInput.left;
5660 pt.y = rectInput.top + newSizeY - sizeY;
5661 ScreenToClient(hDlg, &pt);
5662 SetWindowPos(hInput, NULL,
5663 pt.x, pt.y, /* needs client coords */
5664 rectInput.right - rectInput.left + newSizeX - sizeX,
5665 rectInput.bottom - rectInput.top, SWP_NOZORDER);
5671 case WM_GETMINMAXINFO:
5672 /* Prevent resizing window too small */
5673 mmi = (MINMAXINFO *) lParam;
5674 mmi->ptMinTrackSize.x = 100;
5675 mmi->ptMinTrackSize.y = 100;
5678 return DefWindowProc(hDlg, message, wParam, lParam);
5686 if (hwndConsole) return;
5687 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
5688 SendMessage(hCons, WM_INITDIALOG, 0, 0);
5693 ConsoleOutput(char* data, int length, int forceVisible)
5701 static int delayLF = 0;
5702 CHARRANGE savesel, sel;
5704 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
5720 } else if (*p == '\007') {
5721 MyPlaySound(&sounds[(int)SoundBell]);
5728 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
5729 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5730 /* Save current selection */
5731 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
5732 exlen = GetWindowTextLength(hText);
5733 /* Find out whether current end of text is visible */
5734 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
5735 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
5736 /* Trim existing text if it's too long */
5737 if (exlen + (q - buf) > CO_MAX) {
5738 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
5741 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5742 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
5744 savesel.cpMin -= trim;
5745 savesel.cpMax -= trim;
5746 if (exlen < 0) exlen = 0;
5747 if (savesel.cpMin < 0) savesel.cpMin = 0;
5748 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
5750 /* Append the new text */
5753 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5754 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
5755 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
5756 if (forceVisible || exlen == 0 ||
5757 (rect.left <= pEnd.x && pEnd.x < rect.right &&
5758 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
5759 /* Scroll to make new end of text visible if old end of text
5760 was visible or new text is an echo of user typein */
5761 sel.cpMin = 9999999;
5762 sel.cpMax = 9999999;
5763 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5764 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5765 SendMessage(hText, EM_SCROLLCARET, 0, 0);
5766 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5768 if (savesel.cpMax == exlen || forceVisible) {
5769 /* Move insert point to new end of text if it was at the old
5770 end of text or if the new text is an echo of user typein */
5771 sel.cpMin = 9999999;
5772 sel.cpMax = 9999999;
5773 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5775 /* Restore previous selection */
5776 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
5778 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5785 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
5786 RECT *rect, char *color)
5790 COLORREF oldFg, oldBg;
5793 if (appData.clockMode) {
5795 sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));
5797 sprintf(buf, "%s: %s", color, TimeString(timeRemaining));
5804 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
5805 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
5807 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
5808 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
5810 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
5812 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
5813 rect->top, ETO_CLIPPED|ETO_OPAQUE,
5814 rect, str, strlen(str), NULL);
5816 (void) SetTextColor(hdc, oldFg);
5817 (void) SetBkColor(hdc, oldBg);
5818 (void) SelectObject(hdc, oldFont);
5823 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5828 ResetEvent(ovl->hEvent);
5829 ovl->Offset = ovl->OffsetHigh = 0;
5830 ok = ReadFile(hFile, buf, count, outCount, ovl);
5834 err = GetLastError();
5835 if (err == ERROR_IO_PENDING) {
5836 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5840 err = GetLastError();
5847 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5852 ResetEvent(ovl->hEvent);
5853 ovl->Offset = ovl->OffsetHigh = 0;
5854 ok = WriteFile(hFile, buf, count, outCount, ovl);
5858 err = GetLastError();
5859 if (err == ERROR_IO_PENDING) {
5860 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5864 err = GetLastError();
5872 InputThread(LPVOID arg)
5877 is = (InputSource *) arg;
5878 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
5879 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
5880 while (is->hThread != NULL) {
5881 is->error = DoReadFile(is->hFile, is->next,
5882 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5884 if (is->error == NO_ERROR) {
5885 is->next += is->count;
5887 if (is->error == ERROR_BROKEN_PIPE) {
5888 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5891 is->count = (DWORD) -1;
5894 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5895 if (is->count <= 0) break; /* Quit on EOF or error */
5897 CloseHandle(ovl.hEvent);
5898 CloseHandle(is->hFile);
5903 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
5905 NonOvlInputThread(LPVOID arg)
5912 is = (InputSource *) arg;
5913 while (is->hThread != NULL) {
5914 is->error = ReadFile(is->hFile, is->next,
5915 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5916 &is->count, NULL) ? NO_ERROR : GetLastError();
5917 if (is->error == NO_ERROR) {
5918 /* Change CRLF to LF */
5919 if (is->next > is->buf) {
5929 if (prev == '\r' && *p == '\n') {
5941 if (is->error == ERROR_BROKEN_PIPE) {
5942 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5945 is->count = (DWORD) -1;
5948 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5949 if (is->count < 0) break; /* Quit on error */
5951 CloseHandle(is->hFile);
5956 SocketInputThread(LPVOID arg)
5960 is = (InputSource *) arg;
5961 while (is->hThread != NULL) {
5962 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
5963 if ((int)is->count == SOCKET_ERROR) {
5964 is->count = (DWORD) -1;
5965 is->error = WSAGetLastError();
5967 is->error = NO_ERROR;
5968 is->next += is->count;
5969 if (is->count == 0 && is->second == is) {
5970 /* End of file on stderr; quit with no message */
5974 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5975 if (is->count <= 0) break; /* Quit on EOF or error */
5981 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5985 is = (InputSource *) lParam;
5986 if (is->lineByLine) {
5987 /* Feed in lines one by one */
5990 while (q < is->next) {
5992 (is->func)(is, is->closure, p, q - p, NO_ERROR);
5996 /* Move any partial line to the start of the buffer */
5998 while (p < is->next) {
6002 if (is->error != NO_ERROR || is->count == 0) {
6003 /* Notify backend of the error. Note: If there was a partial
6004 line at the end, it is not flushed through. */
6005 (is->func)(is, is->closure, is->buf, is->count, is->error);
6008 /* Feed in the whole chunk of input at once */
6009 (is->func)(is, is->closure, is->buf, is->count, is->error);
6014 /*---------------------------------------------------------------------------*\
6016 * Menu enables. Used when setting various modes.
6018 \*---------------------------------------------------------------------------*/
6026 SetMenuEnables(HMENU hmenu, Enables *enab)
6028 while (enab->item > 0) {
6029 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
6034 Enables gnuEnables[] = {
6035 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6036 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6037 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6038 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
6039 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
6040 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
6041 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6042 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
6043 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
6044 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6048 Enables icsEnables[] = {
6049 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6050 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6051 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6052 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6053 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6054 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6055 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6056 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6057 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6058 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6059 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6060 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
6065 Enables zippyEnables[] = {
6066 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6067 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
6068 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
6073 Enables ncpEnables[] = {
6074 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6075 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6076 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6077 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6078 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6079 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6080 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6081 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6082 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
6083 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6084 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6085 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6086 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6087 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6088 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6092 Enables trainingOnEnables[] = {
6093 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
6094 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
6095 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
6096 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
6097 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
6098 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
6099 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6100 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
6104 Enables trainingOffEnables[] = {
6105 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
6106 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
6107 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
6108 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
6109 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
6110 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
6111 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6112 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
6116 /* These modify either ncpEnables or gnuEnables */
6117 Enables cmailEnables[] = {
6118 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
6119 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
6120 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
6121 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
6122 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
6123 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6124 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
6128 Enables machineThinkingEnables[] = {
6129 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
6130 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
6131 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
6132 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
6133 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
6134 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
6135 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
6136 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
6137 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
6138 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
6139 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6140 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6141 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6142 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
6143 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6147 Enables userThinkingEnables[] = {
6148 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
6149 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
6150 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
6151 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
6152 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
6153 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
6154 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
6155 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
6156 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
6157 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
6158 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
6159 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
6160 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
6161 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
6162 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
6166 /*---------------------------------------------------------------------------*\
6168 * Front-end interface functions exported by XBoard.
6169 * Functions appear in same order as prototypes in frontend.h.
6171 \*---------------------------------------------------------------------------*/
6175 static UINT prevChecked = 0;
6176 static int prevPausing = 0;
6179 if (pausing != prevPausing) {
6180 prevPausing = pausing;
6181 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
6182 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
6183 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
6187 case BeginningOfGame:
6188 if (appData.icsActive)
6189 nowChecked = IDM_IcsClient;
6190 else if (appData.noChessProgram)
6191 nowChecked = IDM_EditGame;
6193 nowChecked = IDM_MachineBlack;
6195 case MachinePlaysBlack:
6196 nowChecked = IDM_MachineBlack;
6198 case MachinePlaysWhite:
6199 nowChecked = IDM_MachineWhite;
6201 case TwoMachinesPlay:
6202 nowChecked = IDM_TwoMachines;
6205 nowChecked = IDM_AnalysisMode;
6208 nowChecked = IDM_AnalyzeFile;
6211 nowChecked = IDM_EditGame;
6213 case PlayFromGameFile:
6214 nowChecked = IDM_LoadGame;
6217 nowChecked = IDM_EditPosition;
6220 nowChecked = IDM_Training;
6222 case IcsPlayingWhite:
6223 case IcsPlayingBlack:
6226 nowChecked = IDM_IcsClient;
6233 if (prevChecked != 0)
6234 (void) CheckMenuItem(GetMenu(hwndMain),
6235 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
6236 if (nowChecked != 0)
6237 (void) CheckMenuItem(GetMenu(hwndMain),
6238 nowChecked, MF_BYCOMMAND|MF_CHECKED);
6240 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
6241 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
6242 MF_BYCOMMAND|MF_ENABLED);
6244 (void) EnableMenuItem(GetMenu(hwndMain),
6245 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
6248 prevChecked = nowChecked;
6250 /* icsEngineAnalyze - Do a sceure check too */
6251 if (appData.icsActive) {
6252 if (appData.icsEngineAnalyze) {
6253 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6254 MF_BYCOMMAND|MF_CHECKED);
6256 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6257 MF_BYCOMMAND|MF_UNCHECKED);
6265 HMENU hmenu = GetMenu(hwndMain);
6266 SetMenuEnables(hmenu, icsEnables);
6267 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
6268 MF_BYPOSITION|MF_ENABLED);
6270 if (appData.zippyPlay) {
6271 SetMenuEnables(hmenu, zippyEnables);
6272 /* icsEngineAnalyze */
6273 if (!appData.noChessProgram)
6274 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6275 MF_BYCOMMAND|MF_ENABLED);
6283 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
6289 HMENU hmenu = GetMenu(hwndMain);
6290 SetMenuEnables(hmenu, ncpEnables);
6291 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
6292 MF_BYPOSITION|MF_GRAYED);
6293 DrawMenuBar(hwndMain);
6299 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
6306 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
6307 for (i = 0; i < N_BUTTONS; i++) {
6308 if (buttonDesc[i].hwnd != NULL)
6309 EnableWindow(buttonDesc[i].hwnd, FALSE);
6314 VOID SetTrainingModeOff()
6317 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
6318 for (i = 0; i < N_BUTTONS; i++) {
6319 if (buttonDesc[i].hwnd != NULL)
6320 EnableWindow(buttonDesc[i].hwnd, TRUE);
6326 SetUserThinkingEnables()
6328 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
6332 SetMachineThinkingEnables()
6334 HMENU hMenu = GetMenu(hwndMain);
6335 int flags = MF_BYCOMMAND|MF_ENABLED;
6337 SetMenuEnables(hMenu, machineThinkingEnables);
6339 if (gameMode == MachinePlaysBlack) {
6340 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
6341 } else if (gameMode == MachinePlaysWhite) {
6342 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
6343 } else if (gameMode == TwoMachinesPlay) {
6344 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
6350 DisplayTitle(char *str)
6352 char title[MSG_SIZ], *host;
6353 if (str[0] != NULLCHAR) {
6355 } else if (appData.icsActive) {
6356 if (appData.icsCommPort[0] != NULLCHAR)
6359 host = appData.icsHost;
6360 sprintf(title, "%s: %s", szTitle, host);
6361 } else if (appData.noChessProgram) {
6362 strcpy(title, szTitle);
6364 strcpy(title, szTitle);
6365 strcat(title, ": ");
6366 strcat(title, first.tidy);
6368 SetWindowText(hwndMain, title);
6373 DisplayMessage(char *str1, char *str2)
6377 int remain = MESSAGE_TEXT_MAX - 1;
6380 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
6381 messageText[0] = NULLCHAR;
6384 if (len > remain) len = remain;
6385 strncpy(messageText, str1, len);
6386 messageText[len] = NULLCHAR;
6389 if (*str2 && remain >= 2) {
6391 strcat(messageText, " ");
6395 if (len > remain) len = remain;
6396 strncat(messageText, str2, len);
6398 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
6400 if (IsIconic(hwndMain)) return;
6401 hdc = GetDC(hwndMain);
6402 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
6403 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
6404 &messageRect, messageText, strlen(messageText), NULL);
6405 (void) SelectObject(hdc, oldFont);
6406 (void) ReleaseDC(hwndMain, hdc);
6410 DisplayError(char *str, int error)
6412 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
6418 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6419 NULL, error, LANG_NEUTRAL,
6420 (LPSTR) buf2, MSG_SIZ, NULL);
6422 sprintf(buf, "%s:\n%s", str, buf2);
6424 ErrorMap *em = errmap;
6425 while (em->err != 0 && em->err != error) em++;
6427 sprintf(buf, "%s:\n%s", str, em->msg);
6429 sprintf(buf, "%s:\nError code %d", str, error);
6434 ErrorPopUp("Error", buf);
6439 DisplayMoveError(char *str)
6443 DrawPosition(FALSE, NULL);
6444 if (appData.popupMoveErrors) {
6445 ErrorPopUp("Error", str);
6447 DisplayMessage(str, "");
6448 moveErrorMessageUp = TRUE;
6453 DisplayFatalError(char *str, int error, int exitStatus)
6455 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
6457 char *label = exitStatus ? "Fatal Error" : "Exiting";
6460 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6461 NULL, error, LANG_NEUTRAL,
6462 (LPSTR) buf2, MSG_SIZ, NULL);
6464 sprintf(buf, "%s:\n%s", str, buf2);
6466 ErrorMap *em = errmap;
6467 while (em->err != 0 && em->err != error) em++;
6469 sprintf(buf, "%s:\n%s", str, em->msg);
6471 sprintf(buf, "%s:\nError code %d", str, error);
6476 if (appData.debugMode) {
6477 fprintf(debugFP, "%s: %s\n", label, str);
6479 if (appData.popupExitMessage) {
6480 (void) MessageBox(hwndMain, str, label, MB_OK|
6481 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
6483 ExitEvent(exitStatus);
6488 DisplayInformation(char *str)
6490 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
6495 DisplayNote(char *str)
6497 ErrorPopUp("Note", str);
6502 char *title, *question, *replyPrefix;
6507 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
6509 static QuestionParams *qp;
6510 char reply[MSG_SIZ];
6515 qp = (QuestionParams *) lParam;
6516 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
6517 SetWindowText(hDlg, qp->title);
6518 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
6519 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
6523 switch (LOWORD(wParam)) {
6525 strcpy(reply, qp->replyPrefix);
6526 if (*reply) strcat(reply, " ");
6527 len = strlen(reply);
6528 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
6529 strcat(reply, "\n");
6530 OutputToProcess(qp->pr, reply, strlen(reply), &err);
6531 EndDialog(hDlg, TRUE);
6532 if (err) DisplayFatalError("Error writing to chess program", err, 1);
6535 EndDialog(hDlg, FALSE);
6546 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
6552 qp.question = question;
6553 qp.replyPrefix = replyPrefix;
6555 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
6556 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
6557 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
6558 FreeProcInstance(lpProc);
6563 DisplayIcsInteractionTitle(char *str)
6565 char consoleTitle[MSG_SIZ];
6567 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
6568 SetWindowText(hwndConsole, consoleTitle);
6572 DrawPosition(int fullRedraw, Board board)
6574 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
6582 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
6583 dragInfo.pos.x = dragInfo.pos.y = -1;
6584 dragInfo.pos.x = dragInfo.pos.y = -1;
6585 dragInfo.lastpos = dragInfo.pos;
6586 dragInfo.start.x = dragInfo.start.y = -1;
6587 dragInfo.from = dragInfo.start;
6589 DrawPosition(TRUE, NULL);
6595 CommentPopUp(char *title, char *str)
6597 HWND hwnd = GetActiveWindow();
6598 EitherCommentPopUp(0, title, str, FALSE);
6599 SetActiveWindow(hwnd);
6603 CommentPopDown(void)
6605 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
6606 if (commentDialog) {
6607 ShowWindow(commentDialog, SW_HIDE);
6609 commentDialogUp = FALSE;
6613 EditCommentPopUp(int index, char *title, char *str)
6615 EitherCommentPopUp(index, title, str, TRUE);
6622 MyPlaySound(&sounds[(int)SoundMove]);
6625 VOID PlayIcsWinSound()
6627 MyPlaySound(&sounds[(int)SoundIcsWin]);
6630 VOID PlayIcsLossSound()
6632 MyPlaySound(&sounds[(int)SoundIcsLoss]);
6635 VOID PlayIcsDrawSound()
6637 MyPlaySound(&sounds[(int)SoundIcsDraw]);
6640 VOID PlayIcsUnfinishedSound()
6642 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
6648 MyPlaySound(&sounds[(int)SoundAlarm]);
6657 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6658 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
6659 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
6668 consoleEcho = FALSE;
6669 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6670 /* This works OK: set text and background both to the same color */
6672 cf.crTextColor = COLOR_ECHOOFF;
6673 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
6674 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
6679 void Colorize(ColorClass cc, int continuation)
6681 currentColorClass = cc;
6682 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
6683 consoleCF.crTextColor = textAttribs[cc].color;
6684 consoleCF.dwEffects = textAttribs[cc].effects;
6685 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
6691 static char buf[MSG_SIZ];
6692 DWORD bufsiz = MSG_SIZ;
6694 if (!GetUserName(buf, &bufsiz)) {
6695 /*DisplayError("Error getting user name", GetLastError());*/
6696 strcpy(buf, "User");
6704 static char buf[MSG_SIZ];
6705 DWORD bufsiz = MSG_SIZ;
6707 if (!GetComputerName(buf, &bufsiz)) {
6708 /*DisplayError("Error getting host name", GetLastError());*/
6709 strcpy(buf, "Unknown");
6718 return clockTimerEvent != 0;
6724 if (clockTimerEvent == 0) return FALSE;
6725 KillTimer(hwndMain, clockTimerEvent);
6726 clockTimerEvent = 0;
6731 StartClockTimer(long millisec)
6733 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
6734 (UINT) millisec, NULL);
6738 DisplayWhiteClock(long timeRemaining, int highlight)
6741 hdc = GetDC(hwndMain);
6742 if (!IsIconic(hwndMain)) {
6743 DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");
6745 if (highlight && iconCurrent == iconBlack) {
6746 iconCurrent = iconWhite;
6747 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6748 if (IsIconic(hwndMain)) {
6749 DrawIcon(hdc, 2, 2, iconCurrent);
6752 (void) ReleaseDC(hwndMain, hdc);
6754 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6758 DisplayBlackClock(long timeRemaining, int highlight)
6761 hdc = GetDC(hwndMain);
6762 if (!IsIconic(hwndMain)) {
6763 DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");
6765 if (highlight && iconCurrent == iconWhite) {
6766 iconCurrent = iconBlack;
6767 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6768 if (IsIconic(hwndMain)) {
6769 DrawIcon(hdc, 2, 2, iconCurrent);
6772 (void) ReleaseDC(hwndMain, hdc);
6774 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6779 LoadGameTimerRunning()
6781 return loadGameTimerEvent != 0;
6787 if (loadGameTimerEvent == 0) return FALSE;
6788 KillTimer(hwndMain, loadGameTimerEvent);
6789 loadGameTimerEvent = 0;
6794 StartLoadGameTimer(long millisec)
6796 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
6797 (UINT) millisec, NULL);
6805 char fileTitle[MSG_SIZ];
6807 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
6808 f = OpenFileDialog(hwndMain, TRUE, defName,
6809 appData.oldSaveStyle ? "gam" : "pgn",
6811 "Save Game to File", NULL, fileTitle, NULL);
6820 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
6822 if (delayedTimerEvent != 0) {
6823 if (appData.debugMode) {
6824 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
6826 KillTimer(hwndMain, delayedTimerEvent);
6827 delayedTimerEvent = 0;
6828 delayedTimerCallback();
6830 delayedTimerCallback = cb;
6831 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
6832 (UINT) millisec, NULL);
6835 DelayedEventCallback
6838 if (delayedTimerEvent) {
6839 return delayedTimerCallback;
6846 CancelDelayedEvent()
6848 if (delayedTimerEvent) {
6849 KillTimer(hwndMain, delayedTimerEvent);
6850 delayedTimerEvent = 0;
6854 /* Start a child process running the given program.
6855 The process's standard output can be read from "from", and its
6856 standard input can be written to "to".
6857 Exit with fatal error if anything goes wrong.
6858 Returns an opaque pointer that can be used to destroy the process
6862 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
6864 #define BUFSIZE 4096
6866 HANDLE hChildStdinRd, hChildStdinWr,
6867 hChildStdoutRd, hChildStdoutWr;
6868 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
6869 SECURITY_ATTRIBUTES saAttr;
6871 PROCESS_INFORMATION piProcInfo;
6872 STARTUPINFO siStartInfo;
6877 if (appData.debugMode) {
6878 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
6883 /* Set the bInheritHandle flag so pipe handles are inherited. */
6884 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
6885 saAttr.bInheritHandle = TRUE;
6886 saAttr.lpSecurityDescriptor = NULL;
6889 * The steps for redirecting child's STDOUT:
6890 * 1. Create anonymous pipe to be STDOUT for child.
6891 * 2. Create a noninheritable duplicate of read handle,
6892 * and close the inheritable read handle.
6895 /* Create a pipe for the child's STDOUT. */
6896 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
6897 return GetLastError();
6900 /* Duplicate the read handle to the pipe, so it is not inherited. */
6901 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
6902 GetCurrentProcess(), &hChildStdoutRdDup, 0,
6903 FALSE, /* not inherited */
6904 DUPLICATE_SAME_ACCESS);
6906 return GetLastError();
6908 CloseHandle(hChildStdoutRd);
6911 * The steps for redirecting child's STDIN:
6912 * 1. Create anonymous pipe to be STDIN for child.
6913 * 2. Create a noninheritable duplicate of write handle,
6914 * and close the inheritable write handle.
6917 /* Create a pipe for the child's STDIN. */
6918 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
6919 return GetLastError();
6922 /* Duplicate the write handle to the pipe, so it is not inherited. */
6923 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
6924 GetCurrentProcess(), &hChildStdinWrDup, 0,
6925 FALSE, /* not inherited */
6926 DUPLICATE_SAME_ACCESS);
6928 return GetLastError();
6930 CloseHandle(hChildStdinWr);
6932 /* Arrange to (1) look in dir for the child .exe file, and
6933 * (2) have dir be the child's working directory. Interpret
6934 * dir relative to the directory WinBoard loaded from. */
6935 GetCurrentDirectory(MSG_SIZ, buf);
6936 SetCurrentDirectory(installDir);
6937 SetCurrentDirectory(dir);
6939 /* Now create the child process. */
6941 siStartInfo.cb = sizeof(STARTUPINFO);
6942 siStartInfo.lpReserved = NULL;
6943 siStartInfo.lpDesktop = NULL;
6944 siStartInfo.lpTitle = NULL;
6945 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
6946 siStartInfo.cbReserved2 = 0;
6947 siStartInfo.lpReserved2 = NULL;
6948 siStartInfo.hStdInput = hChildStdinRd;
6949 siStartInfo.hStdOutput = hChildStdoutWr;
6950 siStartInfo.hStdError = hChildStdoutWr;
6952 fSuccess = CreateProcess(NULL,
6953 cmdLine, /* command line */
6954 NULL, /* process security attributes */
6955 NULL, /* primary thread security attrs */
6956 TRUE, /* handles are inherited */
6957 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
6958 NULL, /* use parent's environment */
6960 &siStartInfo, /* STARTUPINFO pointer */
6961 &piProcInfo); /* receives PROCESS_INFORMATION */
6963 err = GetLastError();
6964 SetCurrentDirectory(buf); /* return to prev directory */
6969 /* Close the handles we don't need in the parent */
6970 CloseHandle(piProcInfo.hThread);
6971 CloseHandle(hChildStdinRd);
6972 CloseHandle(hChildStdoutWr);
6974 /* Prepare return value */
6975 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6977 cp->hProcess = piProcInfo.hProcess;
6978 cp->pid = piProcInfo.dwProcessId;
6979 cp->hFrom = hChildStdoutRdDup;
6980 cp->hTo = hChildStdinWrDup;
6984 /* Klaus Friedel says that this Sleep solves a problem under Windows
6985 2000 where engines sometimes don't see the initial command(s)
6986 from WinBoard and hang. I don't understand how that can happen,
6987 but the Sleep is harmless, so I've put it in. Others have also
6988 reported what may be the same problem, so hopefully this will fix
6997 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
7001 cp = (ChildProc *) pr;
7002 if (cp == NULL) return;
7006 /* TerminateProcess is considered harmful, so... */
7007 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
7008 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
7009 /* The following doesn't work because the chess program
7010 doesn't "have the same console" as WinBoard. Maybe
7011 we could arrange for this even though neither WinBoard
7012 nor the chess program uses a console for stdio? */
7013 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
7014 CloseHandle(cp->hProcess);
7018 if (cp->hFrom) CloseHandle(cp->hFrom);
7022 closesocket(cp->sock);
7027 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
7028 closesocket(cp->sock);
7029 closesocket(cp->sock2);
7037 InterruptChildProcess(ProcRef pr)
7041 cp = (ChildProc *) pr;
7042 if (cp == NULL) return;
7045 /* The following doesn't work because the chess program
7046 doesn't "have the same console" as WinBoard. Maybe
7047 we could arrange for this even though neither WinBoard
7048 nor the chess program uses a console for stdio */
7049 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
7054 /* Can't interrupt */
7058 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
7065 OpenTelnet(char *host, char *port, ProcRef *pr)
7067 char cmdLine[MSG_SIZ];
7069 if (port[0] == NULLCHAR) {
7070 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
7072 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
7074 return StartChildProcess(cmdLine, "", pr);
7078 /* Code to open TCP sockets */
7081 OpenTCP(char *host, char *port, ProcRef *pr)
7086 struct sockaddr_in sa, mysa;
7087 struct hostent FAR *hp;
7088 unsigned short uport;
7089 WORD wVersionRequested;
7092 /* Initialize socket DLL */
7093 wVersionRequested = MAKEWORD(1, 1);
7094 err = WSAStartup(wVersionRequested, &wsaData);
7095 if (err != 0) return err;
7098 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7099 err = WSAGetLastError();
7104 /* Bind local address using (mostly) don't-care values.
7106 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7107 mysa.sin_family = AF_INET;
7108 mysa.sin_addr.s_addr = INADDR_ANY;
7109 uport = (unsigned short) 0;
7110 mysa.sin_port = htons(uport);
7111 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7113 err = WSAGetLastError();
7118 /* Resolve remote host name */
7119 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7120 if (!(hp = gethostbyname(host))) {
7121 unsigned int b0, b1, b2, b3;
7123 err = WSAGetLastError();
7125 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7126 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7127 hp->h_addrtype = AF_INET;
7129 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7130 hp->h_addr_list[0] = (char *) malloc(4);
7131 hp->h_addr_list[0][0] = (char) b0;
7132 hp->h_addr_list[0][1] = (char) b1;
7133 hp->h_addr_list[0][2] = (char) b2;
7134 hp->h_addr_list[0][3] = (char) b3;
7140 sa.sin_family = hp->h_addrtype;
7141 uport = (unsigned short) atoi(port);
7142 sa.sin_port = htons(uport);
7143 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7145 /* Make connection */
7146 if (connect(s, (struct sockaddr *) &sa,
7147 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7148 err = WSAGetLastError();
7153 /* Prepare return value */
7154 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7157 *pr = (ProcRef *) cp;
7163 OpenCommPort(char *name, ProcRef *pr)
7168 char fullname[MSG_SIZ];
7171 sprintf(fullname, "\\\\.\\%s", name);
7173 strcpy(fullname, name);
7175 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
7176 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
7177 if (h == (HANDLE) -1) {
7178 return GetLastError();
7182 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
7184 /* Accumulate characters until a 100ms pause, then parse */
7185 ct.ReadIntervalTimeout = 100;
7186 ct.ReadTotalTimeoutMultiplier = 0;
7187 ct.ReadTotalTimeoutConstant = 0;
7188 ct.WriteTotalTimeoutMultiplier = 0;
7189 ct.WriteTotalTimeoutConstant = 0;
7190 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
7192 /* Prepare return value */
7193 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7197 *pr = (ProcRef *) cp;
7203 OpenLoopback(ProcRef *pr)
7205 DisplayFatalError("Not implemented", 0, 1);
7211 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
7216 struct sockaddr_in sa, mysa;
7217 struct hostent FAR *hp;
7218 unsigned short uport;
7219 WORD wVersionRequested;
7222 char stderrPortStr[MSG_SIZ];
7224 /* Initialize socket DLL */
7225 wVersionRequested = MAKEWORD(1, 1);
7226 err = WSAStartup(wVersionRequested, &wsaData);
7227 if (err != 0) return err;
7229 /* Resolve remote host name */
7230 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7231 if (!(hp = gethostbyname(host))) {
7232 unsigned int b0, b1, b2, b3;
7234 err = WSAGetLastError();
7236 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7237 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7238 hp->h_addrtype = AF_INET;
7240 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7241 hp->h_addr_list[0] = (char *) malloc(4);
7242 hp->h_addr_list[0][0] = (char) b0;
7243 hp->h_addr_list[0][1] = (char) b1;
7244 hp->h_addr_list[0][2] = (char) b2;
7245 hp->h_addr_list[0][3] = (char) b3;
7251 sa.sin_family = hp->h_addrtype;
7252 uport = (unsigned short) 514;
7253 sa.sin_port = htons(uport);
7254 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7256 /* Bind local socket to unused "privileged" port address
7259 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7260 mysa.sin_family = AF_INET;
7261 mysa.sin_addr.s_addr = INADDR_ANY;
7262 for (fromPort = 1023;; fromPort--) {
7265 return WSAEADDRINUSE;
7267 if (s == INVALID_SOCKET) {
7268 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7269 err = WSAGetLastError();
7274 uport = (unsigned short) fromPort;
7275 mysa.sin_port = htons(uport);
7276 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7278 err = WSAGetLastError();
7279 if (err == WSAEADDRINUSE) continue;
7283 if (connect(s, (struct sockaddr *) &sa,
7284 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7285 err = WSAGetLastError();
7286 if (err == WSAEADDRINUSE) {
7297 /* Bind stderr local socket to unused "privileged" port address
7299 s2 = INVALID_SOCKET;
7300 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7301 mysa.sin_family = AF_INET;
7302 mysa.sin_addr.s_addr = INADDR_ANY;
7303 for (fromPort = 1023;; fromPort--) {
7304 if (fromPort == prevStderrPort) continue; // don't reuse port
7306 (void) closesocket(s);
7308 return WSAEADDRINUSE;
7310 if (s2 == INVALID_SOCKET) {
7311 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7312 err = WSAGetLastError();
7318 uport = (unsigned short) fromPort;
7319 mysa.sin_port = htons(uport);
7320 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7322 err = WSAGetLastError();
7323 if (err == WSAEADDRINUSE) continue;
7324 (void) closesocket(s);
7328 if (listen(s2, 1) == SOCKET_ERROR) {
7329 err = WSAGetLastError();
7330 if (err == WSAEADDRINUSE) {
7332 s2 = INVALID_SOCKET;
7335 (void) closesocket(s);
7336 (void) closesocket(s2);
7342 prevStderrPort = fromPort; // remember port used
7343 sprintf(stderrPortStr, "%d", fromPort);
7345 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
7346 err = WSAGetLastError();
7347 (void) closesocket(s);
7348 (void) closesocket(s2);
7353 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
7354 err = WSAGetLastError();
7355 (void) closesocket(s);
7356 (void) closesocket(s2);
7360 if (*user == NULLCHAR) user = UserName();
7361 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
7362 err = WSAGetLastError();
7363 (void) closesocket(s);
7364 (void) closesocket(s2);
7368 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
7369 err = WSAGetLastError();
7370 (void) closesocket(s);
7371 (void) closesocket(s2);
7376 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
7377 err = WSAGetLastError();
7378 (void) closesocket(s);
7379 (void) closesocket(s2);
7383 (void) closesocket(s2); /* Stop listening */
7385 /* Prepare return value */
7386 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7390 *pr = (ProcRef *) cp;
7397 AddInputSource(ProcRef pr, int lineByLine,
7398 InputCallback func, VOIDSTAR closure)
7400 InputSource *is, *is2;
7401 ChildProc *cp = (ChildProc *) pr;
7403 is = (InputSource *) calloc(1, sizeof(InputSource));
7404 is->lineByLine = lineByLine;
7406 is->closure = closure;
7411 consoleInputSource = is;
7413 is->kind = cp->kind;
7416 is->hFile = cp->hFrom;
7417 cp->hFrom = NULL; /* now owned by InputThread */
7419 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
7420 (LPVOID) is, 0, &is->id);
7424 is->hFile = cp->hFrom;
7425 cp->hFrom = NULL; /* now owned by InputThread */
7427 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
7428 (LPVOID) is, 0, &is->id);
7432 is->sock = cp->sock;
7434 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7435 (LPVOID) is, 0, &is->id);
7439 is2 = (InputSource *) calloc(1, sizeof(InputSource));
7441 is->sock = cp->sock;
7443 is2->sock = cp->sock2;
7446 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7447 (LPVOID) is, 0, &is->id);
7449 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7450 (LPVOID) is2, 0, &is2->id);
7454 return (InputSourceRef) is;
7458 RemoveInputSource(InputSourceRef isr)
7462 is = (InputSource *) isr;
7463 is->hThread = NULL; /* tell thread to stop */
7464 CloseHandle(is->hThread);
7465 if (is->second != NULL) {
7466 is->second->hThread = NULL;
7467 CloseHandle(is->second->hThread);
7473 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
7476 int outCount = SOCKET_ERROR;
7477 ChildProc *cp = (ChildProc *) pr;
7478 static OVERLAPPED ovl;
7481 ConsoleOutput(message, count, FALSE);
7485 if (ovl.hEvent == NULL) {
7486 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
7488 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
7493 outCount = send(cp->sock, message, count, 0);
7494 if (outCount == SOCKET_ERROR) {
7495 *outError = WSAGetLastError();
7497 *outError = NO_ERROR;
7502 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
7503 &dOutCount, NULL)) {
7504 *outError = NO_ERROR;
7505 outCount = (int) dOutCount;
7507 *outError = GetLastError();
7512 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
7514 if (*outError == NO_ERROR) {
7515 outCount = (int) dOutCount;
7523 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
7526 /* Ignore delay, not implemented for WinBoard */
7527 return OutputToProcess(pr, message, count, outError);
7532 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
7533 char *buf, int count, int error)
7535 DisplayFatalError("Not implemented", 0, 1);
7538 /* see wgamelist.c for Game List functions */
7539 /* see wedittags.c for Edit Tags functions */
7549 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
7550 f = fopen(buf, "r");
7552 ProcessICSInitScript(f);
7560 StartAnalysisClock()
7562 if (analysisTimerEvent) return;
7563 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
7568 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
7570 static HANDLE hwndText;
7572 static int sizeX, sizeY;
7573 int newSizeX, newSizeY, flags;
7577 case WM_INITDIALOG: /* message: initialize dialog box */
7578 /* Initialize the dialog items */
7579 hwndText = GetDlgItem(hDlg, OPT_AnalysisText);
7580 SetWindowText(hDlg, analysisTitle);
7581 SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);
7582 /* Size and position the dialog */
7583 if (!analysisDialog) {
7584 analysisDialog = hDlg;
7585 flags = SWP_NOZORDER;
7586 GetClientRect(hDlg, &rect);
7588 sizeY = rect.bottom;
7589 if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&
7590 analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {
7592 EnsureOnScreen(&analysisX, &analysisY);
7593 wp.length = sizeof(WINDOWPLACEMENT);
7595 wp.showCmd = SW_SHOW;
7596 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
7597 wp.rcNormalPosition.left = analysisX;
7598 wp.rcNormalPosition.right = analysisX + analysisW;
7599 wp.rcNormalPosition.top = analysisY;
7600 wp.rcNormalPosition.bottom = analysisY + analysisH;
7601 SetWindowPlacement(hDlg, &wp);
7603 GetClientRect(hDlg, &rect);
7604 newSizeX = rect.right;
7605 newSizeY = rect.bottom;
7606 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
7607 newSizeX, newSizeY);
7614 case WM_COMMAND: /* message: received a command */
7615 switch (LOWORD(wParam)) {
7625 newSizeX = LOWORD(lParam);
7626 newSizeY = HIWORD(lParam);
7627 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
7632 case WM_GETMINMAXINFO:
7633 /* Prevent resizing window too small */
7634 mmi = (MINMAXINFO *) lParam;
7635 mmi->ptMinTrackSize.x = 100;
7636 mmi->ptMinTrackSize.y = 100;
7643 AnalysisPopUp(char* title, char* str)
7648 if (str == NULL) str = "";
7649 p = (char *) malloc(2 * strlen(str) + 2);
7652 if (*str == '\n') *q++ = '\r';
7656 if (analysisText != NULL) free(analysisText);
7659 if (analysisDialog) {
7660 SetWindowText(analysisDialog, title);
7661 SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);
7662 ShowWindow(analysisDialog, SW_SHOW);
7664 analysisTitle = title;
7665 lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);
7666 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),
7667 hwndMain, (DLGPROC)lpProc);
7668 FreeProcInstance(lpProc);
7670 analysisDialogUp = TRUE;
7676 if (analysisDialog) {
7677 ShowWindow(analysisDialog, SW_HIDE);
7679 analysisDialogUp = FALSE;
7684 SetHighlights(int fromX, int fromY, int toX, int toY)
7686 highlightInfo.sq[0].x = fromX;
7687 highlightInfo.sq[0].y = fromY;
7688 highlightInfo.sq[1].x = toX;
7689 highlightInfo.sq[1].y = toY;
7695 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
7696 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
7700 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
7702 premoveHighlightInfo.sq[0].x = fromX;
7703 premoveHighlightInfo.sq[0].y = fromY;
7704 premoveHighlightInfo.sq[1].x = toX;
7705 premoveHighlightInfo.sq[1].y = toY;
7709 ClearPremoveHighlights()
7711 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
7712 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
7718 if (saveSettingsOnExit) SaveSettings(settingsFileName);
7719 DeleteClipboardTempFiles();
7725 if (IsIconic(hwndMain))
7726 ShowWindow(hwndMain, SW_RESTORE);
7728 SetActiveWindow(hwndMain);
7732 * Prototypes for animation support routines
7734 static void ScreenSquare(int column, int row, POINT * pt);
7735 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
7736 POINT frames[], int * nFrames);
7742 AnimateMove(board, fromX, fromY, toX, toY)
7750 POINT start, finish, mid;
7751 POINT frames[kFactor * 2 + 1];
7754 if (!appData.animate) return;
7755 if (doingSizing) return;
7756 if (fromY < 0 || fromX < 0) return;
7757 piece = board[fromY][fromX];
7758 if (piece >= EmptySquare) return;
7760 ScreenSquare(fromX, fromY, &start);
7761 ScreenSquare(toX, toY, &finish);
7763 /* All pieces except knights move in straight line */
7764 if (piece != WhiteKnight && piece != BlackKnight) {
7765 mid.x = start.x + (finish.x - start.x) / 2;
7766 mid.y = start.y + (finish.y - start.y) / 2;
7768 /* Knight: make diagonal movement then straight */
7769 if (abs(toY - fromY) < abs(toX - fromX)) {
7770 mid.x = start.x + (finish.x - start.x) / 2;
7774 mid.y = start.y + (finish.y - start.y) / 2;
7778 /* Don't use as many frames for very short moves */
7779 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7780 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7782 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7784 animInfo.from.x = fromX;
7785 animInfo.from.y = fromY;
7786 animInfo.to.x = toX;
7787 animInfo.to.y = toY;
7788 animInfo.lastpos = start;
7789 animInfo.piece = piece;
7790 for (n = 0; n < nFrames; n++) {
7791 animInfo.pos = frames[n];
7792 DrawPosition(FALSE, NULL);
7793 animInfo.lastpos = animInfo.pos;
7794 Sleep(appData.animSpeed);
7796 animInfo.pos = finish;
7797 DrawPosition(FALSE, NULL);
7798 animInfo.piece = EmptySquare;
7801 /* Convert board position to corner of screen rect and color */
7804 ScreenSquare(column, row, pt)
7805 int column; int row; POINT * pt;
7808 pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
7809 pt->y = lineGap + row * (squareSize + lineGap);
7811 pt->x = lineGap + column * (squareSize + lineGap);
7812 pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
7816 /* Generate a series of frame coords from start->mid->finish.
7817 The movement rate doubles until the half way point is
7818 reached, then halves back down to the final destination,
7819 which gives a nice slow in/out effect. The algorithmn
7820 may seem to generate too many intermediates for short
7821 moves, but remember that the purpose is to attract the
7822 viewers attention to the piece about to be moved and
7823 then to where it ends up. Too few frames would be less
7827 Tween(start, mid, finish, factor, frames, nFrames)
7828 POINT * start; POINT * mid;
7829 POINT * finish; int factor;
7830 POINT frames[]; int * nFrames;
7832 int n, fraction = 1, count = 0;
7834 /* Slow in, stepping 1/16th, then 1/8th, ... */
7835 for (n = 0; n < factor; n++)
7837 for (n = 0; n < factor; n++) {
7838 frames[count].x = start->x + (mid->x - start->x) / fraction;
7839 frames[count].y = start->y + (mid->y - start->y) / fraction;
7841 fraction = fraction / 2;
7845 frames[count] = *mid;
7848 /* Slow out, stepping 1/2, then 1/4, ... */
7850 for (n = 0; n < factor; n++) {
7851 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
7852 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
7854 fraction = fraction * 2;
7860 HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
7862 /* Currently not implemented in WinBoard */