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 appData.icsEngineAnalyze = FALSE;
3660 /* if enable, user want disable icsEngineAnalyze */
3661 if (appData.icsEngineAnalyze) {
3662 appData.icsEngineAnalyze = FALSE;
3667 appData.icsEngineAnalyze = TRUE;
3670 if (!appData.showThinking) ToggleShowThinking();
3674 case IDM_AnalyzeFile:
3675 if (!first.analysisSupport) {
3677 sprintf(buf, "%s does not support analysis", first.tidy);
3678 DisplayError(buf, 0);
3680 if (!appData.showThinking) ToggleShowThinking();
3682 LoadGameDialog(hwnd, "Analyze Game from File");
3683 AnalysisPeriodicEvent(1);
3695 case IDM_EditPosition:
3696 EditPositionEvent();
3703 case IDM_ShowGameList:
3711 case IDM_EditComment:
3712 if (commentDialogUp && editComment) {
3755 case IDM_StopObserving:
3756 StopObservingEvent();
3759 case IDM_StopExamining:
3760 StopExaminingEvent();
3763 case IDM_TypeInMove:
3764 PopUpMoveDialog('\000');
3791 case IDM_TruncateGame:
3792 TruncateGameEvent();
3799 case IDM_RetractMove:
3804 flipView = !flipView;
3805 DrawPosition(FALSE, NULL);
3808 case IDM_GeneralOptions:
3809 GeneralOptionsPopup(hwnd);
3812 case IDM_BoardOptions:
3813 BoardOptionsPopup(hwnd);
3816 case IDM_IcsOptions:
3817 IcsOptionsPopup(hwnd);
3821 FontsOptionsPopup(hwnd);
3825 SoundOptionsPopup(hwnd);
3829 CommPortOptionsPopup(hwnd);
3832 case IDM_LoadOptions:
3833 LoadOptionsPopup(hwnd);
3836 case IDM_SaveOptions:
3837 SaveOptionsPopup(hwnd);
3840 case IDM_TimeControl:
3841 TimeControlOptionsPopup(hwnd);
3844 case IDM_SaveSettings:
3845 SaveSettings(settingsFileName);
3848 case IDM_SaveSettingsOnExit:
3849 saveSettingsOnExit = !saveSettingsOnExit;
3850 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
3851 MF_BYCOMMAND|(saveSettingsOnExit ?
3852 MF_CHECKED : MF_UNCHECKED));
3868 appData.debugMode = !appData.debugMode;
3869 if (appData.debugMode) {
3871 GetCurrentDirectory(MSG_SIZ, dir);
3872 SetCurrentDirectory(installDir);
3873 debugFP = fopen("WinBoard.debug", "w");
3874 SetCurrentDirectory(dir);
3875 setbuf(debugFP, NULL);
3882 case IDM_HELPCONTENTS:
3883 if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
3884 MessageBox (GetFocus(),
3885 "Unable to activate help",
3886 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3890 case IDM_HELPSEARCH:
3891 if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
3892 MessageBox (GetFocus(),
3893 "Unable to activate help",
3894 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3899 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
3900 MessageBox (GetFocus(),
3901 "Unable to activate help",
3902 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3907 lpProc = MakeProcInstance((FARPROC)About, hInst);
3909 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
3910 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
3911 FreeProcInstance(lpProc);
3914 case IDM_DirectCommand1:
3915 AskQuestionEvent("Direct Command",
3916 "Send to chess program:", "", "1");
3918 case IDM_DirectCommand2:
3919 AskQuestionEvent("Direct Command",
3920 "Send to second chess program:", "", "2");
3924 EditPositionMenuEvent(WhitePawn, fromX, fromY);
3928 case EP_WhiteKnight:
3929 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
3933 case EP_WhiteBishop:
3934 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
3939 EditPositionMenuEvent(WhiteRook, fromX, fromY);
3944 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
3949 EditPositionMenuEvent(WhiteKing, fromX, fromY);
3954 EditPositionMenuEvent(BlackPawn, fromX, fromY);
3958 case EP_BlackKnight:
3959 EditPositionMenuEvent(BlackKnight, fromX, fromY);
3963 case EP_BlackBishop:
3964 EditPositionMenuEvent(BlackBishop, fromX, fromY);
3969 EditPositionMenuEvent(BlackRook, fromX, fromY);
3974 EditPositionMenuEvent(BlackQueen, fromX, fromY);
3979 EditPositionMenuEvent(BlackKing, fromX, fromY);
3983 case EP_EmptySquare:
3984 EditPositionMenuEvent(EmptySquare, fromX, fromY);
3989 EditPositionMenuEvent(ClearBoard, fromX, fromY);
3994 EditPositionMenuEvent(WhitePlay, fromX, fromY);
3999 EditPositionMenuEvent(BlackPlay, fromX, fromY);
4004 DropMenuEvent(WhitePawn, fromX, fromY);
4009 DropMenuEvent(WhiteKnight, fromX, fromY);
4014 DropMenuEvent(WhiteBishop, fromX, fromY);
4019 DropMenuEvent(WhiteRook, fromX, fromY);
4024 DropMenuEvent(WhiteQueen, fromX, fromY);
4029 return (DefWindowProc(hwnd, message, wParam, lParam));
4035 case CLOCK_TIMER_ID:
4036 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
4037 clockTimerEvent = 0;
4038 DecrementClocks(); /* call into back end */
4040 case LOAD_GAME_TIMER_ID:
4041 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
4042 loadGameTimerEvent = 0;
4043 AutoPlayGameLoop(); /* call into back end */
4045 case ANALYSIS_TIMER_ID:
4046 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
4047 || appData.icsEngineAnalyze) && appData.periodicUpdates) {
4048 AnalysisPeriodicEvent(0);
4050 KillTimer(hwnd, analysisTimerEvent);
4051 analysisTimerEvent = 0;
4054 case DELAYED_TIMER_ID:
4055 KillTimer(hwnd, delayedTimerEvent);
4056 delayedTimerEvent = 0;
4057 delayedTimerCallback();
4063 InputEvent(hwnd, message, wParam, lParam);
4066 case WM_ENTERSIZEMOVE:
4067 if (hwnd == hwndMain) {
4074 if (hwnd == hwndMain) {
4075 lastSizing = wParam;
4079 case WM_EXITSIZEMOVE:
4080 if (hwnd == hwndMain) {
4082 doingSizing = FALSE;
4083 InvalidateRect(hwnd, &boardRect, FALSE);
4084 GetClientRect(hwnd, &client);
4085 ResizeBoard(client.right, client.bottom, lastSizing);
4090 case WM_DESTROY: /* message: window being destroyed */
4095 if (hwnd == hwndMain) {
4100 default: /* Passes it on if unprocessed */
4101 return (DefWindowProc(hwnd, message, wParam, lParam));
4106 /*---------------------------------------------------------------------------*\
4108 * Misc utility routines
4110 \*---------------------------------------------------------------------------*/
4113 * Decent random number generator, at least not as bad as Windows
4114 * standard rand, which returns a value in the range 0 to 0x7fff.
4116 unsigned int randstate;
4121 randstate = randstate * 1664525 + 1013904223;
4122 return (int) randstate & 0x7fffffff;
4126 mysrandom(unsigned int seed)
4133 * returns TRUE if user selects a different color, FALSE otherwise
4137 ChangeColor(HWND hwnd, COLORREF *which)
4139 static BOOL firstTime = TRUE;
4140 static DWORD customColors[16];
4147 /* Make initial colors in use available as custom colors */
4148 /* Should we put the compiled-in defaults here instead? */
4150 customColors[i++] = lightSquareColor & 0xffffff;
4151 customColors[i++] = darkSquareColor & 0xffffff;
4152 customColors[i++] = whitePieceColor & 0xffffff;
4153 customColors[i++] = blackPieceColor & 0xffffff;
4154 customColors[i++] = highlightSquareColor & 0xffffff;
4155 customColors[i++] = premoveHighlightColor & 0xffffff;
4157 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
4158 customColors[i++] = textAttribs[ccl].color;
4160 while (i < 16) customColors[i++] = RGB(255, 255, 255);
4164 cc.lStructSize = sizeof(cc);
4165 cc.hwndOwner = hwnd;
4166 cc.hInstance = NULL;
4167 cc.rgbResult = (DWORD) (*which & 0xffffff);
4168 cc.lpCustColors = (LPDWORD) customColors;
4169 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
4171 if (!ChooseColor(&cc)) return FALSE;
4173 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
4174 if (newcolor == *which) return FALSE;
4179 InitDrawingColors();
4180 InvalidateRect(hwnd, &boardRect, FALSE);
4185 MyLoadSound(MySound *ms)
4191 if (ms->data) free(ms->data);
4194 switch (ms->name[0]) {
4200 /* System sound from Control Panel. Don't preload here. */
4204 if (ms->name[1] == NULLCHAR) {
4205 /* "!" alone = silence */
4208 /* Builtin wave resource. Error if not found. */
4209 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
4210 if (h == NULL) break;
4211 ms->data = (void *)LoadResource(hInst, h);
4212 if (h == NULL) break;
4217 /* .wav file. Error if not found. */
4218 f = fopen(ms->name, "rb");
4219 if (f == NULL) break;
4220 if (fstat(fileno(f), &st) < 0) break;
4221 ms->data = malloc(st.st_size);
4222 if (fread(ms->data, st.st_size, 1, f) < 1) break;
4229 sprintf(buf, "Error loading sound %s", ms->name);
4230 DisplayError(buf, GetLastError());
4236 MyPlaySound(MySound *ms)
4239 switch (ms->name[0]) {
4245 /* System sound from Control Panel (deprecated feature).
4246 "$" alone or an unset sound name gets default beep (still in use). */
4248 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
4250 if (!ok) ok = MessageBeep(MB_OK);
4253 /* Builtin wave resource, or "!" alone for silence */
4255 if (ms->data == NULL) return FALSE;
4256 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4262 /* .wav file. Error if not found. */
4263 if (ms->data == NULL) return FALSE;
4264 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4267 /* Don't print an error: this can happen innocently if the sound driver
4268 is busy; for instance, if another instance of WinBoard is playing
4269 a sound at about the same time. */
4273 sprintf(buf, "Error playing sound %s", ms->name);
4274 DisplayError(buf, GetLastError());
4282 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4286 static UINT *number; /* gross that this is static */
4289 case WM_INITDIALOG: /* message: initialize dialog box */
4290 /* Center the dialog over the application window */
4291 ofn = (OPENFILENAME *) lParam;
4292 if (ofn->Flags & OFN_ENABLETEMPLATE) {
4293 number = (UINT *) ofn->lCustData;
4294 SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
4298 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4299 return FALSE; /* Allow for further processing */
4302 if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
4303 *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
4305 return FALSE; /* Allow for further processing */
4311 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
4313 static UINT *number;
4314 OPENFILENAME *ofname;
4318 ofname = (OPENFILENAME *)lParam;
4319 number = (UINT *)(ofname->lCustData);
4322 ofnot = (OFNOTIFY *)lParam;
4323 if (ofnot->hdr.code == CDN_FILEOK) {
4324 *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
4333 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,
4334 char *nameFilt, char *dlgTitle, UINT *number,
4335 char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
4337 OPENFILENAME openFileName;
4341 if (fileName == NULL) fileName = buf1;
4342 if (defName == NULL) {
4343 strcpy(fileName, "*.");
4344 strcat(fileName, defExt);
4346 strcpy(fileName, defName);
4348 if (fileTitle) strcpy(fileTitle, "");
4349 if (number) *number = 0;
4351 openFileName.lStructSize = sizeof(OPENFILENAME);
4352 openFileName.hwndOwner = hwnd;
4353 openFileName.hInstance = (HANDLE) hInst;
4354 openFileName.lpstrFilter = nameFilt;
4355 openFileName.lpstrCustomFilter = (LPSTR) NULL;
4356 openFileName.nMaxCustFilter = 0L;
4357 openFileName.nFilterIndex = 1L;
4358 openFileName.lpstrFile = fileName;
4359 openFileName.nMaxFile = MSG_SIZ;
4360 openFileName.lpstrFileTitle = fileTitle;
4361 openFileName.nMaxFileTitle = fileTitle ? MSG_SIZ : 0;
4362 openFileName.lpstrInitialDir = NULL;
4363 openFileName.lpstrTitle = dlgTitle;
4364 openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY
4365 | (write ? 0 : OFN_FILEMUSTEXIST)
4366 | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
4367 | (oldDialog ? 0 : OFN_EXPLORER);
4368 openFileName.nFileOffset = 0;
4369 openFileName.nFileExtension = 0;
4370 openFileName.lpstrDefExt = defExt;
4371 openFileName.lCustData = (LONG) number;
4372 openFileName.lpfnHook = oldDialog ?
4373 (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
4374 openFileName.lpTemplateName = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
4376 if (write ? GetSaveFileName(&openFileName) :
4377 GetOpenFileName(&openFileName)) {
4379 f = fopen(openFileName.lpstrFile, write ? "a" : "rb");
4381 MessageBox(hwnd, "File open failed", NULL,
4382 MB_OK|MB_ICONEXCLAMATION);
4386 int err = CommDlgExtendedError();
4387 if (err != 0) DisplayError("Internal error in file dialog box", err);
4396 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
4398 HMENU hmenuTrackPopup; /* floating pop-up menu */
4401 * Get the first pop-up menu in the menu template. This is the
4402 * menu that TrackPopupMenu displays.
4404 hmenuTrackPopup = GetSubMenu(hmenu, 0);
4406 SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
4409 * TrackPopup uses screen coordinates, so convert the
4410 * coordinates of the mouse click to screen coordinates.
4412 ClientToScreen(hwnd, (LPPOINT) &pt);
4414 /* Draw and track the floating pop-up menu. */
4415 TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
4416 pt.x, pt.y, 0, hwnd, NULL);
4418 /* Destroy the menu.*/
4424 int sizeX, sizeY, newSizeX, newSizeY;
4426 } ResizeEditPlusButtonsClosure;
4429 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
4431 ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
4435 if (hChild == cl->hText) return TRUE;
4436 GetWindowRect(hChild, &rect); /* gives screen coords */
4437 pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
4438 pt.y = rect.top + cl->newSizeY - cl->sizeY;
4439 ScreenToClient(cl->hDlg, &pt);
4440 cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL,
4441 pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
4445 /* Resize a dialog that has a (rich) edit field filling most of
4446 the top, with a row of buttons below */
4448 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
4451 int newTextHeight, newTextWidth;
4452 ResizeEditPlusButtonsClosure cl;
4454 /*if (IsIconic(hDlg)) return;*/
4455 if (newSizeX == sizeX && newSizeY == sizeY) return;
4457 cl.hdwp = BeginDeferWindowPos(8);
4459 GetWindowRect(hText, &rectText); /* gives screen coords */
4460 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
4461 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
4462 if (newTextHeight < 0) {
4463 newSizeY += -newTextHeight;
4466 cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0,
4467 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
4473 cl.newSizeX = newSizeX;
4474 cl.newSizeY = newSizeY;
4475 EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
4477 EndDeferWindowPos(cl.hdwp);
4480 /* Center one window over another */
4481 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
4483 RECT rChild, rParent;
4484 int wChild, hChild, wParent, hParent;
4485 int wScreen, hScreen, xNew, yNew;
4488 /* Get the Height and Width of the child window */
4489 GetWindowRect (hwndChild, &rChild);
4490 wChild = rChild.right - rChild.left;
4491 hChild = rChild.bottom - rChild.top;
4493 /* Get the Height and Width of the parent window */
4494 GetWindowRect (hwndParent, &rParent);
4495 wParent = rParent.right - rParent.left;
4496 hParent = rParent.bottom - rParent.top;
4498 /* Get the display limits */
4499 hdc = GetDC (hwndChild);
4500 wScreen = GetDeviceCaps (hdc, HORZRES);
4501 hScreen = GetDeviceCaps (hdc, VERTRES);
4502 ReleaseDC(hwndChild, hdc);
4504 /* Calculate new X position, then adjust for screen */
4505 xNew = rParent.left + ((wParent - wChild) /2);
4508 } else if ((xNew+wChild) > wScreen) {
4509 xNew = wScreen - wChild;
4512 /* Calculate new Y position, then adjust for screen */
4513 yNew = rParent.top + ((hParent - hChild) /2);
4516 } else if ((yNew+hChild) > hScreen) {
4517 yNew = hScreen - hChild;
4520 /* Set it, and return */
4521 return SetWindowPos (hwndChild, NULL,
4522 xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4525 /*---------------------------------------------------------------------------*\
4527 * Startup Dialog functions
4529 \*---------------------------------------------------------------------------*/
4531 InitComboStrings(HANDLE hwndCombo, char **cd)
4533 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4535 while (*cd != NULL) {
4536 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
4542 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
4547 if (str[0] == '@') {
4548 FILE* f = fopen(str + 1, "r");
4550 DisplayFatalError(str + 1, errno, 2);
4553 len = fread(buf1, 1, sizeof(buf1)-1, f);
4555 buf1[len] = NULLCHAR;
4559 SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4563 char *end = strchr(str, '\n');
4564 if (end == NULL) return;
4565 memcpy(buf, str, end - str);
4566 buf[end - str] = NULLCHAR;
4567 SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
4573 SetStartupDialogEnables(HWND hDlg)
4575 EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
4576 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4577 appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));
4578 EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
4579 IsDlgButtonChecked(hDlg, OPT_ChessEngine));
4580 EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
4581 IsDlgButtonChecked(hDlg, OPT_ChessServer));
4582 EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
4583 IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
4584 EnableWindow(GetDlgItem(hDlg, IDOK),
4585 IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4586 IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
4587 IsDlgButtonChecked(hDlg, OPT_View));
4591 QuoteForFilename(char *filename)
4594 dquote = strchr(filename, '"') != NULL;
4595 space = strchr(filename, ' ') != NULL;
4596 if (dquote || space) {
4608 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
4613 InitComboStringsFromOption(hwndCombo, nthnames);
4614 q = QuoteForFilename(nthcp);
4615 sprintf(buf, "%s%s%s", q, nthcp, q);
4616 if (*nthdir != NULLCHAR) {
4617 q = QuoteForFilename(nthdir);
4618 sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
4620 if (*nthcp == NULLCHAR) {
4621 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4622 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4623 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4624 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4629 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4637 /* Center the dialog */
4638 CenterWindow (hDlg, GetDesktopWindow());
4639 /* Initialize the dialog items */
4640 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
4641 appData.firstChessProgram, "fd", appData.firstDirectory,
4642 firstChessProgramNames);
4643 InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
4644 appData.secondChessProgram, "sd", appData.secondDirectory,
4645 secondChessProgramNames);
4646 hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
4647 InitComboStringsFromOption(hwndCombo, icsNames);
4648 sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
4649 if (*appData.icsHelper != NULLCHAR) {
4650 char *q = QuoteForFilename(appData.icsHelper);
4651 sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
4653 if (*appData.icsHost == NULLCHAR) {
4654 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4655 /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
4656 } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4657 SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4658 SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4661 CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
4662 } else if (appData.icsActive) {
4663 CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
4664 } else if (appData.noChessProgram) {
4665 CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
4667 SetStartupDialogEnables(hDlg);
4671 switch (LOWORD(wParam)) {
4673 if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
4674 strcpy(buf, "/fcp=");
4675 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4677 ParseArgs(StringGet, &p);
4678 strcpy(buf, "/scp=");
4679 GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4681 ParseArgs(StringGet, &p);
4682 appData.noChessProgram = FALSE;
4683 appData.icsActive = FALSE;
4684 } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
4685 strcpy(buf, "/ics /icshost=");
4686 GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4688 ParseArgs(StringGet, &p);
4689 if (appData.zippyPlay) {
4690 strcpy(buf, "/fcp=");
4691 GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4693 ParseArgs(StringGet, &p);
4695 } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
4696 appData.noChessProgram = TRUE;
4697 appData.icsActive = FALSE;
4699 MessageBox(hDlg, "Choose an option, or cancel to exit",
4700 "Option Error", MB_OK|MB_ICONEXCLAMATION);
4703 if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
4704 GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
4706 ParseArgs(StringGet, &p);
4708 EndDialog(hDlg, TRUE);
4715 case IDM_HELPCONTENTS:
4716 if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
4717 MessageBox (GetFocus(),
4718 "Unable to activate help",
4719 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
4724 SetStartupDialogEnables(hDlg);
4732 /*---------------------------------------------------------------------------*\
4734 * About box dialog functions
4736 \*---------------------------------------------------------------------------*/
4738 /* Process messages for "About" dialog box */
4740 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4743 case WM_INITDIALOG: /* message: initialize dialog box */
4744 /* Center the dialog over the application window */
4745 CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4746 SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
4749 case WM_COMMAND: /* message: received a command */
4750 if (LOWORD(wParam) == IDOK /* "OK" box selected? */
4751 || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
4752 EndDialog(hDlg, TRUE); /* Exit the dialog */
4760 /*---------------------------------------------------------------------------*\
4762 * Comment Dialog functions
4764 \*---------------------------------------------------------------------------*/
4767 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4769 static HANDLE hwndText = NULL;
4770 int len, newSizeX, newSizeY, flags;
4771 static int sizeX, sizeY;
4777 case WM_INITDIALOG: /* message: initialize dialog box */
4778 /* Initialize the dialog items */
4779 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4780 SetDlgItemText(hDlg, OPT_CommentText, commentText);
4781 EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
4782 EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
4783 EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
4784 SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
4785 SetWindowText(hDlg, commentTitle);
4789 SetFocus(GetDlgItem(hDlg, IDOK));
4791 SendMessage(GetDlgItem(hDlg, OPT_CommentText),
4792 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
4793 MAKELPARAM(FALSE, 0));
4794 /* Size and position the dialog */
4795 if (!commentDialog) {
4796 commentDialog = hDlg;
4797 flags = SWP_NOZORDER;
4798 GetClientRect(hDlg, &rect);
4800 sizeY = rect.bottom;
4801 if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&
4802 commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {
4804 EnsureOnScreen(&commentX, &commentY);
4805 wp.length = sizeof(WINDOWPLACEMENT);
4807 wp.showCmd = SW_SHOW;
4808 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
4809 wp.rcNormalPosition.left = commentX;
4810 wp.rcNormalPosition.right = commentX + commentW;
4811 wp.rcNormalPosition.top = commentY;
4812 wp.rcNormalPosition.bottom = commentY + commentH;
4813 SetWindowPlacement(hDlg, &wp);
4815 GetClientRect(hDlg, &rect);
4816 newSizeX = rect.right;
4817 newSizeY = rect.bottom;
4818 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
4819 newSizeX, newSizeY);
4826 case WM_COMMAND: /* message: received a command */
4827 switch (LOWORD(wParam)) {
4831 /* Read changed options from the dialog box */
4832 hwndText = GetDlgItem(hDlg, OPT_CommentText);
4833 len = GetWindowTextLength(hwndText);
4834 str = (char *) malloc(len + 1);
4835 GetWindowText(hwndText, str, len + 1);
4844 ReplaceComment(commentIndex, str);
4851 case OPT_CancelComment:
4855 case OPT_ClearComment:
4856 SetDlgItemText(hDlg, OPT_CommentText, "");
4859 case OPT_EditComment:
4869 newSizeX = LOWORD(lParam);
4870 newSizeY = HIWORD(lParam);
4871 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
4876 case WM_GETMINMAXINFO:
4877 /* Prevent resizing window too small */
4878 mmi = (MINMAXINFO *) lParam;
4879 mmi->ptMinTrackSize.x = 100;
4880 mmi->ptMinTrackSize.y = 100;
4887 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
4892 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
4894 if (str == NULL) str = "";
4895 p = (char *) malloc(2 * strlen(str) + 2);
4898 if (*str == '\n') *q++ = '\r';
4902 if (commentText != NULL) free(commentText);
4904 commentIndex = index;
4905 commentTitle = title;
4909 if (commentDialog) {
4910 SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
4911 if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);
4913 lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
4914 CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
4915 hwndMain, (DLGPROC)lpProc);
4916 FreeProcInstance(lpProc);
4918 commentDialogUp = TRUE;
4922 /*---------------------------------------------------------------------------*\
4924 * Type-in move dialog functions
4926 \*---------------------------------------------------------------------------*/
4929 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4934 int fromX, fromY, toX, toY;
4939 move[0] = (char) lParam;
4941 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
4942 hInput = GetDlgItem(hDlg, OPT_Move);
4943 SetWindowText(hInput, move);
4945 SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
4949 switch (LOWORD(wParam)) {
4951 if (gameMode != EditGame && currentMove != forwardMostMove &&
4952 gameMode != Training) {
4953 DisplayMoveError("Displayed move is not current");
4955 GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
4956 if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
4957 &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
4958 if (gameMode != Training)
4959 forwardMostMove = currentMove;
4960 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4962 DisplayMoveError("Could not parse move");
4965 EndDialog(hDlg, TRUE);
4968 EndDialog(hDlg, FALSE);
4979 PopUpMoveDialog(char firstchar)
4983 if ((gameMode == BeginningOfGame && !appData.icsActive) ||
4984 gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
4985 gameMode == AnalyzeMode || gameMode == EditGame ||
4986 gameMode == EditPosition || gameMode == IcsExamining ||
4987 gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
4988 gameMode == Training) {
4989 lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
4990 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
4991 hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
4992 FreeProcInstance(lpProc);
4996 /*---------------------------------------------------------------------------*\
5000 \*---------------------------------------------------------------------------*/
5002 /* Nonmodal error box */
5003 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
5004 WPARAM wParam, LPARAM lParam);
5007 ErrorPopUp(char *title, char *content)
5011 BOOLEAN modal = hwndMain == NULL;
5029 strncpy(errorTitle, title, sizeof(errorTitle));
5030 errorTitle[sizeof(errorTitle) - 1] = '\0';
5033 MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
5035 lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
5036 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
5037 hwndMain, (DLGPROC)lpProc);
5038 FreeProcInstance(lpProc);
5045 if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
5046 if (errorDialog == NULL) return;
5047 DestroyWindow(errorDialog);
5052 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5059 GetWindowRect(hDlg, &rChild);
5060 SetWindowPos(hDlg, NULL, rChild.left,
5061 rChild.top + boardRect.top - (rChild.bottom - rChild.top),
5062 0, 0, SWP_NOZORDER|SWP_NOSIZE);
5064 SetWindowText(hDlg, errorTitle);
5065 hwndText = GetDlgItem(hDlg, OPT_ErrorText);
5066 SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
5070 switch (LOWORD(wParam)) {
5073 if (errorDialog == hDlg) errorDialog = NULL;
5074 DestroyWindow(hDlg);
5085 /*---------------------------------------------------------------------------*\
5087 * Ics Interaction console functions
5089 \*---------------------------------------------------------------------------*/
5091 #define HISTORY_SIZE 64
5092 static char *history[HISTORY_SIZE];
5093 int histIn = 0, histP = 0;
5096 SaveInHistory(char *cmd)
5098 if (history[histIn] != NULL) {
5099 free(history[histIn]);
5100 history[histIn] = NULL;
5102 if (*cmd == NULLCHAR) return;
5103 history[histIn] = StrSave(cmd);
5104 histIn = (histIn + 1) % HISTORY_SIZE;
5105 if (history[histIn] != NULL) {
5106 free(history[histIn]);
5107 history[histIn] = NULL;
5113 PrevInHistory(char *cmd)
5116 if (histP == histIn) {
5117 if (history[histIn] != NULL) free(history[histIn]);
5118 history[histIn] = StrSave(cmd);
5120 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
5121 if (newhp == histIn || history[newhp] == NULL) return NULL;
5123 return history[histP];
5129 if (histP == histIn) return NULL;
5130 histP = (histP + 1) % HISTORY_SIZE;
5131 return history[histP];
5140 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
5141 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];
5144 ParseIcsTextMenu(char *icsTextMenuString)
5147 IcsTextMenuEntry *e = icsTextMenuEntry;
5148 char *p = icsTextMenuString;
5149 while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5152 if (e->command != NULL) {
5158 e = icsTextMenuEntry;
5159 while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5160 if (*p == ';' || *p == '\n') {
5161 e->item = strdup("-");
5164 } else if (*p == '-') {
5165 e->item = strdup("-");
5170 char *q, *r, *s, *t;
5173 if (q == NULL) break;
5175 r = strchr(q + 1, ',');
5176 if (r == NULL) break;
5178 s = strchr(r + 1, ',');
5179 if (s == NULL) break;
5182 t = strchr(s + 1, c);
5185 t = strchr(s + 1, c);
5187 if (t != NULL) *t = NULLCHAR;
5188 e->item = strdup(p);
5189 e->command = strdup(q + 1);
5190 e->getname = *(r + 1) != '0';
5191 e->immediate = *(s + 1) != '0';
5195 if (t == NULL) break;
5204 LoadIcsTextMenu(IcsTextMenuEntry *e)
5208 hmenu = LoadMenu(hInst, "TextMenu");
5209 h = GetSubMenu(hmenu, 0);
5211 if (strcmp(e->item, "-") == 0) {
5212 AppendMenu(h, MF_SEPARATOR, 0, 0);
5214 if (e->item[0] == '|') {
5215 AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
5216 IDM_CommandX + i, &e->item[1]);
5218 AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
5227 WNDPROC consoleTextWindowProc;
5230 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
5232 char buf[MSG_SIZ], name[MSG_SIZ];
5233 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5237 SetWindowText(hInput, command);
5239 SendMessage(hInput, WM_CHAR, '\r', 0);
5243 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5248 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5249 if (sel.cpMin == sel.cpMax) {
5250 /* Expand to surrounding word */
5253 tr.chrg.cpMax = sel.cpMin;
5254 tr.chrg.cpMin = --sel.cpMin;
5255 if (sel.cpMin < 0) break;
5256 tr.lpstrText = name;
5257 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5258 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5262 tr.chrg.cpMin = sel.cpMax;
5263 tr.chrg.cpMax = ++sel.cpMax;
5264 tr.lpstrText = name;
5265 if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
5266 } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5269 if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5270 MessageBeep(MB_ICONEXCLAMATION);
5274 tr.lpstrText = name;
5275 SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5277 if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5278 MessageBeep(MB_ICONEXCLAMATION);
5281 SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
5284 sprintf(buf, "%s %s", command, name);
5285 SetWindowText(hInput, buf);
5286 SendMessage(hInput, WM_CHAR, '\r', 0);
5288 sprintf(buf, "%s %s ", command, name); /* trailing space */
5289 SetWindowText(hInput, buf);
5292 SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5298 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5305 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5308 SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
5313 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5314 SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
5319 if (wParam == '\t') {
5320 if (GetKeyState(VK_SHIFT) < 0) {
5322 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5323 if (buttonDesc[0].hwnd) {
5324 SetFocus(buttonDesc[0].hwnd);
5330 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
5333 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5335 SendMessage(hInput, message, wParam, lParam);
5339 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5341 return SendMessage(hInput, message, wParam, lParam);
5342 case WM_MBUTTONDOWN:
5343 return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5344 case WM_RBUTTONDOWN:
5345 if (!(GetKeyState(VK_SHIFT) & ~1)) {
5346 /* Move selection here if it was empty */
5348 pt.x = LOWORD(lParam);
5349 pt.y = HIWORD(lParam);
5350 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5351 if (sel.cpMin == sel.cpMax) {
5352 sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
5353 sel.cpMax = sel.cpMin;
5354 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5356 SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
5360 if (GetKeyState(VK_SHIFT) & ~1) {
5361 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5362 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5365 HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
5366 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5367 if (sel.cpMin == sel.cpMax) {
5368 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5369 EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
5371 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5372 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5374 pt.x = LOWORD(lParam);
5375 pt.y = HIWORD(lParam);
5376 MenuPopup(hwnd, pt, hmenu, -1);
5380 switch (LOWORD(wParam)) {
5381 case IDM_QuickPaste:
5383 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5384 if (sel.cpMin == sel.cpMax) {
5385 MessageBeep(MB_ICONEXCLAMATION);
5388 SendMessage(hwnd, WM_COPY, 0, 0);
5389 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5390 SendMessage(hInput, WM_PASTE, 0, 0);
5395 SendMessage(hwnd, WM_CUT, 0, 0);
5398 SendMessage(hwnd, WM_PASTE, 0, 0);
5401 SendMessage(hwnd, WM_COPY, 0, 0);
5405 int i = LOWORD(wParam) - IDM_CommandX;
5406 if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
5407 icsTextMenuEntry[i].command != NULL) {
5408 CommandX(hwnd, icsTextMenuEntry[i].command,
5409 icsTextMenuEntry[i].getname,
5410 icsTextMenuEntry[i].immediate);
5418 return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
5421 WNDPROC consoleInputWindowProc;
5424 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5428 static BOOL sendNextChar = FALSE;
5429 static BOOL quoteNextChar = FALSE;
5430 InputSource *is = consoleInputSource;
5436 if (!appData.localLineEditing || sendNextChar) {
5437 is->buf[0] = (CHAR) wParam;
5439 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5440 sendNextChar = FALSE;
5443 if (quoteNextChar) {
5444 buf[0] = (char) wParam;
5446 SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
5447 quoteNextChar = FALSE;
5451 case '\r': /* Enter key */
5452 is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);
5453 if (consoleEcho) SaveInHistory(is->buf);
5454 is->buf[is->count++] = '\n';
5455 is->buf[is->count] = NULLCHAR;
5456 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5458 ConsoleOutput(is->buf, is->count, TRUE);
5459 } else if (appData.localLineEditing) {
5460 ConsoleOutput("\n", 1, TRUE);
5463 case '\033': /* Escape key */
5464 SetWindowText(hwnd, "");
5465 cf.cbSize = sizeof(CHARFORMAT);
5466 cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
5468 cf.crTextColor = textAttribs[ColorNormal].color;
5470 cf.crTextColor = COLOR_ECHOOFF;
5472 cf.dwEffects = textAttribs[ColorNormal].effects;
5473 SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
5475 case '\t': /* Tab key */
5476 if (GetKeyState(VK_SHIFT) < 0) {
5478 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
5481 if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5482 if (buttonDesc[0].hwnd) {
5483 SetFocus(buttonDesc[0].hwnd);
5489 case '\023': /* Ctrl+S */
5490 sendNextChar = TRUE;
5492 case '\021': /* Ctrl+Q */
5493 quoteNextChar = TRUE;
5502 GetWindowText(hwnd, buf, MSG_SIZ);
5503 p = PrevInHistory(buf);
5505 SetWindowText(hwnd, p);
5508 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5513 p = NextInHistory();
5515 SetWindowText(hwnd, p);
5518 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5524 if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5528 SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
5532 case WM_MBUTTONDOWN:
5533 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5534 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5537 if (GetKeyState(VK_SHIFT) & ~1) {
5538 SendDlgItemMessage(hwndConsole, OPT_ConsoleText,
5539 WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5543 hmenu = LoadMenu(hInst, "InputMenu");
5544 SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5545 if (sel.cpMin == sel.cpMax) {
5546 EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5547 EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
5549 if (!IsClipboardFormatAvailable(CF_TEXT)) {
5550 EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5552 pt.x = LOWORD(lParam);
5553 pt.y = HIWORD(lParam);
5554 MenuPopup(hwnd, pt, hmenu, -1);
5558 switch (LOWORD(wParam)) {
5560 SendMessage(hwnd, EM_UNDO, 0, 0);
5564 sel.cpMax = -1; /*999999?*/
5565 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5568 SendMessage(hwnd, WM_CUT, 0, 0);
5571 SendMessage(hwnd, WM_PASTE, 0, 0);
5574 SendMessage(hwnd, WM_COPY, 0, 0);
5579 return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
5582 #define CO_MAX 100000
5583 #define CO_TRIM 1000
5586 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5588 static HWND hText, hInput, hFocus;
5589 InputSource *is = consoleInputSource;
5591 static int sizeX, sizeY;
5592 int newSizeX, newSizeY;
5596 case WM_INITDIALOG: /* message: initialize dialog box */
5598 hText = GetDlgItem(hDlg, OPT_ConsoleText);
5599 hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
5601 consoleTextWindowProc = (WNDPROC)
5602 SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
5603 SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5604 consoleInputWindowProc = (WNDPROC)
5605 SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
5606 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5607 Colorize(ColorNormal, TRUE);
5608 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
5609 ChangedConsoleFont();
5610 GetClientRect(hDlg, &rect);
5612 sizeY = rect.bottom;
5613 if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&
5614 consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {
5616 EnsureOnScreen(&consoleX, &consoleY);
5617 wp.length = sizeof(WINDOWPLACEMENT);
5619 wp.showCmd = SW_SHOW;
5620 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
5621 wp.rcNormalPosition.left = consoleX;
5622 wp.rcNormalPosition.right = consoleX + consoleW;
5623 wp.rcNormalPosition.top = consoleY;
5624 wp.rcNormalPosition.bottom = consoleY + consoleH;
5625 SetWindowPlacement(hDlg, &wp);
5639 if (IsIconic(hDlg)) break;
5640 newSizeX = LOWORD(lParam);
5641 newSizeY = HIWORD(lParam);
5642 if (sizeX != newSizeX || sizeY != newSizeY) {
5643 RECT rectText, rectInput;
5645 int newTextHeight, newTextWidth;
5646 GetWindowRect(hText, &rectText);
5647 newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
5648 newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
5649 if (newTextHeight < 0) {
5650 newSizeY += -newTextHeight;
5653 SetWindowPos(hText, NULL, 0, 0,
5654 newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
5655 GetWindowRect(hInput, &rectInput); /* gives screen coords */
5656 pt.x = rectInput.left;
5657 pt.y = rectInput.top + newSizeY - sizeY;
5658 ScreenToClient(hDlg, &pt);
5659 SetWindowPos(hInput, NULL,
5660 pt.x, pt.y, /* needs client coords */
5661 rectInput.right - rectInput.left + newSizeX - sizeX,
5662 rectInput.bottom - rectInput.top, SWP_NOZORDER);
5668 case WM_GETMINMAXINFO:
5669 /* Prevent resizing window too small */
5670 mmi = (MINMAXINFO *) lParam;
5671 mmi->ptMinTrackSize.x = 100;
5672 mmi->ptMinTrackSize.y = 100;
5675 return DefWindowProc(hDlg, message, wParam, lParam);
5683 if (hwndConsole) return;
5684 hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
5685 SendMessage(hCons, WM_INITDIALOG, 0, 0);
5690 ConsoleOutput(char* data, int length, int forceVisible)
5698 static int delayLF = 0;
5699 CHARRANGE savesel, sel;
5701 if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
5717 } else if (*p == '\007') {
5718 MyPlaySound(&sounds[(int)SoundBell]);
5725 hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
5726 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5727 /* Save current selection */
5728 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
5729 exlen = GetWindowTextLength(hText);
5730 /* Find out whether current end of text is visible */
5731 SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
5732 SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
5733 /* Trim existing text if it's too long */
5734 if (exlen + (q - buf) > CO_MAX) {
5735 trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
5738 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5739 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
5741 savesel.cpMin -= trim;
5742 savesel.cpMax -= trim;
5743 if (exlen < 0) exlen = 0;
5744 if (savesel.cpMin < 0) savesel.cpMin = 0;
5745 if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
5747 /* Append the new text */
5750 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5751 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
5752 SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
5753 if (forceVisible || exlen == 0 ||
5754 (rect.left <= pEnd.x && pEnd.x < rect.right &&
5755 rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
5756 /* Scroll to make new end of text visible if old end of text
5757 was visible or new text is an echo of user typein */
5758 sel.cpMin = 9999999;
5759 sel.cpMax = 9999999;
5760 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5761 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5762 SendMessage(hText, EM_SCROLLCARET, 0, 0);
5763 SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5765 if (savesel.cpMax == exlen || forceVisible) {
5766 /* Move insert point to new end of text if it was at the old
5767 end of text or if the new text is an echo of user typein */
5768 sel.cpMin = 9999999;
5769 sel.cpMax = 9999999;
5770 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5772 /* Restore previous selection */
5773 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
5775 SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5782 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
5783 RECT *rect, char *color)
5787 COLORREF oldFg, oldBg;
5790 if (appData.clockMode) {
5792 sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));
5794 sprintf(buf, "%s: %s", color, TimeString(timeRemaining));
5801 oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
5802 oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
5804 oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
5805 oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
5807 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
5809 ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
5810 rect->top, ETO_CLIPPED|ETO_OPAQUE,
5811 rect, str, strlen(str), NULL);
5813 (void) SetTextColor(hdc, oldFg);
5814 (void) SetBkColor(hdc, oldBg);
5815 (void) SelectObject(hdc, oldFont);
5820 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5825 ResetEvent(ovl->hEvent);
5826 ovl->Offset = ovl->OffsetHigh = 0;
5827 ok = ReadFile(hFile, buf, count, outCount, ovl);
5831 err = GetLastError();
5832 if (err == ERROR_IO_PENDING) {
5833 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5837 err = GetLastError();
5844 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5849 ResetEvent(ovl->hEvent);
5850 ovl->Offset = ovl->OffsetHigh = 0;
5851 ok = WriteFile(hFile, buf, count, outCount, ovl);
5855 err = GetLastError();
5856 if (err == ERROR_IO_PENDING) {
5857 ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5861 err = GetLastError();
5869 InputThread(LPVOID arg)
5874 is = (InputSource *) arg;
5875 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
5876 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
5877 while (is->hThread != NULL) {
5878 is->error = DoReadFile(is->hFile, is->next,
5879 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5881 if (is->error == NO_ERROR) {
5882 is->next += is->count;
5884 if (is->error == ERROR_BROKEN_PIPE) {
5885 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5888 is->count = (DWORD) -1;
5891 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5892 if (is->count <= 0) break; /* Quit on EOF or error */
5894 CloseHandle(ovl.hEvent);
5895 CloseHandle(is->hFile);
5900 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
5902 NonOvlInputThread(LPVOID arg)
5909 is = (InputSource *) arg;
5910 while (is->hThread != NULL) {
5911 is->error = ReadFile(is->hFile, is->next,
5912 INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5913 &is->count, NULL) ? NO_ERROR : GetLastError();
5914 if (is->error == NO_ERROR) {
5915 /* Change CRLF to LF */
5916 if (is->next > is->buf) {
5926 if (prev == '\r' && *p == '\n') {
5938 if (is->error == ERROR_BROKEN_PIPE) {
5939 /* Correct for MS brain damage. EOF reading a pipe is not an error. */
5942 is->count = (DWORD) -1;
5945 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5946 if (is->count < 0) break; /* Quit on error */
5948 CloseHandle(is->hFile);
5953 SocketInputThread(LPVOID arg)
5957 is = (InputSource *) arg;
5958 while (is->hThread != NULL) {
5959 is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
5960 if ((int)is->count == SOCKET_ERROR) {
5961 is->count = (DWORD) -1;
5962 is->error = WSAGetLastError();
5964 is->error = NO_ERROR;
5965 is->next += is->count;
5966 if (is->count == 0 && is->second == is) {
5967 /* End of file on stderr; quit with no message */
5971 SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5972 if (is->count <= 0) break; /* Quit on EOF or error */
5978 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5982 is = (InputSource *) lParam;
5983 if (is->lineByLine) {
5984 /* Feed in lines one by one */
5987 while (q < is->next) {
5989 (is->func)(is, is->closure, p, q - p, NO_ERROR);
5993 /* Move any partial line to the start of the buffer */
5995 while (p < is->next) {
5999 if (is->error != NO_ERROR || is->count == 0) {
6000 /* Notify backend of the error. Note: If there was a partial
6001 line at the end, it is not flushed through. */
6002 (is->func)(is, is->closure, is->buf, is->count, is->error);
6005 /* Feed in the whole chunk of input at once */
6006 (is->func)(is, is->closure, is->buf, is->count, is->error);
6011 /*---------------------------------------------------------------------------*\
6013 * Menu enables. Used when setting various modes.
6015 \*---------------------------------------------------------------------------*/
6023 SetMenuEnables(HMENU hmenu, Enables *enab)
6025 while (enab->item > 0) {
6026 (void) EnableMenuItem(hmenu, enab->item, enab->flags);
6031 Enables gnuEnables[] = {
6032 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6033 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6034 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6035 { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
6036 { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
6037 { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
6038 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6039 { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
6040 { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
6041 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6045 Enables icsEnables[] = {
6046 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6047 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6048 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6049 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6050 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6051 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6052 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6053 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6054 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6055 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6056 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6057 { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
6062 Enables zippyEnables[] = {
6063 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6064 { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
6065 { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
6070 Enables ncpEnables[] = {
6071 { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6072 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6073 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6074 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6075 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6076 { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6077 { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6078 { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6079 { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
6080 { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6081 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6082 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6083 { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6084 { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6085 { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6089 Enables trainingOnEnables[] = {
6090 { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
6091 { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
6092 { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
6093 { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
6094 { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
6095 { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
6096 { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6097 { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
6101 Enables trainingOffEnables[] = {
6102 { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
6103 { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
6104 { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
6105 { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
6106 { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
6107 { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
6108 { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6109 { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
6113 /* These modify either ncpEnables or gnuEnables */
6114 Enables cmailEnables[] = {
6115 { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
6116 { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
6117 { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
6118 { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
6119 { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
6120 { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6121 { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
6125 Enables machineThinkingEnables[] = {
6126 { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
6127 { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
6128 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
6129 { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
6130 { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
6131 { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
6132 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
6133 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
6134 { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
6135 { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
6136 { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6137 { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6138 { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6139 { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
6140 { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6144 Enables userThinkingEnables[] = {
6145 { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
6146 { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
6147 { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
6148 { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
6149 { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
6150 { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
6151 { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
6152 { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
6153 { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
6154 { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
6155 { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
6156 { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
6157 { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
6158 { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
6159 { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
6163 /*---------------------------------------------------------------------------*\
6165 * Front-end interface functions exported by XBoard.
6166 * Functions appear in same order as prototypes in frontend.h.
6168 \*---------------------------------------------------------------------------*/
6172 static UINT prevChecked = 0;
6173 static int prevPausing = 0;
6176 if (pausing != prevPausing) {
6177 prevPausing = pausing;
6178 (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
6179 MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
6180 if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
6184 case BeginningOfGame:
6185 if (appData.icsActive)
6186 nowChecked = IDM_IcsClient;
6187 else if (appData.noChessProgram)
6188 nowChecked = IDM_EditGame;
6190 nowChecked = IDM_MachineBlack;
6192 case MachinePlaysBlack:
6193 nowChecked = IDM_MachineBlack;
6195 case MachinePlaysWhite:
6196 nowChecked = IDM_MachineWhite;
6198 case TwoMachinesPlay:
6199 nowChecked = IDM_TwoMachines;
6202 nowChecked = IDM_AnalysisMode;
6205 nowChecked = IDM_AnalyzeFile;
6208 nowChecked = IDM_EditGame;
6210 case PlayFromGameFile:
6211 nowChecked = IDM_LoadGame;
6214 nowChecked = IDM_EditPosition;
6217 nowChecked = IDM_Training;
6219 case IcsPlayingWhite:
6220 case IcsPlayingBlack:
6223 nowChecked = IDM_IcsClient;
6230 if (prevChecked != 0)
6231 (void) CheckMenuItem(GetMenu(hwndMain),
6232 prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
6233 if (nowChecked != 0)
6234 (void) CheckMenuItem(GetMenu(hwndMain),
6235 nowChecked, MF_BYCOMMAND|MF_CHECKED);
6237 if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
6238 (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training,
6239 MF_BYCOMMAND|MF_ENABLED);
6241 (void) EnableMenuItem(GetMenu(hwndMain),
6242 IDM_Training, MF_BYCOMMAND|MF_GRAYED);
6245 prevChecked = nowChecked;
6246 /* icsEngineAnalyze - Do a sceure check too */
6247 if (appData.icsEngineAnalyze) {
6248 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6249 MF_BYCOMMAND|MF_CHECKED);
6251 (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6252 MF_BYCOMMAND|MF_UNCHECKED);
6259 HMENU hmenu = GetMenu(hwndMain);
6260 SetMenuEnables(hmenu, icsEnables);
6261 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
6262 MF_BYPOSITION|MF_ENABLED);
6264 if (appData.zippyPlay) {
6265 SetMenuEnables(hmenu, zippyEnables);
6266 /* icsEngineAnalyze */
6267 if (!appData.noChessProgram)
6268 (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6269 MF_BYCOMMAND|MF_ENABLED);
6277 SetMenuEnables(GetMenu(hwndMain), gnuEnables);
6283 HMENU hmenu = GetMenu(hwndMain);
6284 SetMenuEnables(hmenu, ncpEnables);
6285 EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
6286 MF_BYPOSITION|MF_GRAYED);
6287 DrawMenuBar(hwndMain);
6293 SetMenuEnables(GetMenu(hwndMain), cmailEnables);
6300 SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
6301 for (i = 0; i < N_BUTTONS; i++) {
6302 if (buttonDesc[i].hwnd != NULL)
6303 EnableWindow(buttonDesc[i].hwnd, FALSE);
6308 VOID SetTrainingModeOff()
6311 SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
6312 for (i = 0; i < N_BUTTONS; i++) {
6313 if (buttonDesc[i].hwnd != NULL)
6314 EnableWindow(buttonDesc[i].hwnd, TRUE);
6320 SetUserThinkingEnables()
6322 SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
6326 SetMachineThinkingEnables()
6328 HMENU hMenu = GetMenu(hwndMain);
6329 int flags = MF_BYCOMMAND|MF_ENABLED;
6331 SetMenuEnables(hMenu, machineThinkingEnables);
6333 if (gameMode == MachinePlaysBlack) {
6334 (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
6335 } else if (gameMode == MachinePlaysWhite) {
6336 (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
6337 } else if (gameMode == TwoMachinesPlay) {
6338 (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
6344 DisplayTitle(char *str)
6346 char title[MSG_SIZ], *host;
6347 if (str[0] != NULLCHAR) {
6349 } else if (appData.icsActive) {
6350 if (appData.icsCommPort[0] != NULLCHAR)
6353 host = appData.icsHost;
6354 sprintf(title, "%s: %s", szTitle, host);
6355 } else if (appData.noChessProgram) {
6356 strcpy(title, szTitle);
6358 strcpy(title, szTitle);
6359 strcat(title, ": ");
6360 strcat(title, first.tidy);
6362 SetWindowText(hwndMain, title);
6367 DisplayMessage(char *str1, char *str2)
6371 int remain = MESSAGE_TEXT_MAX - 1;
6374 moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
6375 messageText[0] = NULLCHAR;
6378 if (len > remain) len = remain;
6379 strncpy(messageText, str1, len);
6380 messageText[len] = NULLCHAR;
6383 if (*str2 && remain >= 2) {
6385 strcat(messageText, " ");
6389 if (len > remain) len = remain;
6390 strncat(messageText, str2, len);
6392 messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
6394 if (IsIconic(hwndMain)) return;
6395 hdc = GetDC(hwndMain);
6396 oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
6397 ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
6398 &messageRect, messageText, strlen(messageText), NULL);
6399 (void) SelectObject(hdc, oldFont);
6400 (void) ReleaseDC(hwndMain, hdc);
6404 DisplayError(char *str, int error)
6406 char buf[MSG_SIZ*2], buf2[MSG_SIZ];
6412 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6413 NULL, error, LANG_NEUTRAL,
6414 (LPSTR) buf2, MSG_SIZ, NULL);
6416 sprintf(buf, "%s:\n%s", str, buf2);
6418 ErrorMap *em = errmap;
6419 while (em->err != 0 && em->err != error) em++;
6421 sprintf(buf, "%s:\n%s", str, em->msg);
6423 sprintf(buf, "%s:\nError code %d", str, error);
6428 ErrorPopUp("Error", buf);
6433 DisplayMoveError(char *str)
6437 DrawPosition(FALSE, NULL);
6438 if (appData.popupMoveErrors) {
6439 ErrorPopUp("Error", str);
6441 DisplayMessage(str, "");
6442 moveErrorMessageUp = TRUE;
6447 DisplayFatalError(char *str, int error, int exitStatus)
6449 char buf[2*MSG_SIZ], buf2[MSG_SIZ];
6451 char *label = exitStatus ? "Fatal Error" : "Exiting";
6454 len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6455 NULL, error, LANG_NEUTRAL,
6456 (LPSTR) buf2, MSG_SIZ, NULL);
6458 sprintf(buf, "%s:\n%s", str, buf2);
6460 ErrorMap *em = errmap;
6461 while (em->err != 0 && em->err != error) em++;
6463 sprintf(buf, "%s:\n%s", str, em->msg);
6465 sprintf(buf, "%s:\nError code %d", str, error);
6470 if (appData.debugMode) {
6471 fprintf(debugFP, "%s: %s\n", label, str);
6473 if (appData.popupExitMessage) {
6474 (void) MessageBox(hwndMain, str, label, MB_OK|
6475 (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
6477 ExitEvent(exitStatus);
6482 DisplayInformation(char *str)
6484 (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
6489 DisplayNote(char *str)
6491 ErrorPopUp("Note", str);
6496 char *title, *question, *replyPrefix;
6501 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
6503 static QuestionParams *qp;
6504 char reply[MSG_SIZ];
6509 qp = (QuestionParams *) lParam;
6510 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
6511 SetWindowText(hDlg, qp->title);
6512 SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
6513 SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
6517 switch (LOWORD(wParam)) {
6519 strcpy(reply, qp->replyPrefix);
6520 if (*reply) strcat(reply, " ");
6521 len = strlen(reply);
6522 GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
6523 strcat(reply, "\n");
6524 OutputToProcess(qp->pr, reply, strlen(reply), &err);
6525 EndDialog(hDlg, TRUE);
6526 if (err) DisplayFatalError("Error writing to chess program", err, 1);
6529 EndDialog(hDlg, FALSE);
6540 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
6546 qp.question = question;
6547 qp.replyPrefix = replyPrefix;
6549 lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
6550 DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
6551 hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
6552 FreeProcInstance(lpProc);
6557 DisplayIcsInteractionTitle(char *str)
6559 char consoleTitle[MSG_SIZ];
6561 sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
6562 SetWindowText(hwndConsole, consoleTitle);
6566 DrawPosition(int fullRedraw, Board board)
6568 HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board);
6576 if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
6577 dragInfo.pos.x = dragInfo.pos.y = -1;
6578 dragInfo.pos.x = dragInfo.pos.y = -1;
6579 dragInfo.lastpos = dragInfo.pos;
6580 dragInfo.start.x = dragInfo.start.y = -1;
6581 dragInfo.from = dragInfo.start;
6583 DrawPosition(TRUE, NULL);
6589 CommentPopUp(char *title, char *str)
6591 HWND hwnd = GetActiveWindow();
6592 EitherCommentPopUp(0, title, str, FALSE);
6593 SetActiveWindow(hwnd);
6597 CommentPopDown(void)
6599 CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
6600 if (commentDialog) {
6601 ShowWindow(commentDialog, SW_HIDE);
6603 commentDialogUp = FALSE;
6607 EditCommentPopUp(int index, char *title, char *str)
6609 EitherCommentPopUp(index, title, str, TRUE);
6616 MyPlaySound(&sounds[(int)SoundMove]);
6619 VOID PlayIcsWinSound()
6621 MyPlaySound(&sounds[(int)SoundIcsWin]);
6624 VOID PlayIcsLossSound()
6626 MyPlaySound(&sounds[(int)SoundIcsLoss]);
6629 VOID PlayIcsDrawSound()
6631 MyPlaySound(&sounds[(int)SoundIcsDraw]);
6634 VOID PlayIcsUnfinishedSound()
6636 MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
6642 MyPlaySound(&sounds[(int)SoundAlarm]);
6651 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6652 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
6653 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
6662 consoleEcho = FALSE;
6663 hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6664 /* This works OK: set text and background both to the same color */
6666 cf.crTextColor = COLOR_ECHOOFF;
6667 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
6668 SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
6673 void Colorize(ColorClass cc, int continuation)
6675 currentColorClass = cc;
6676 consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
6677 consoleCF.crTextColor = textAttribs[cc].color;
6678 consoleCF.dwEffects = textAttribs[cc].effects;
6679 if (!continuation) MyPlaySound(&textAttribs[cc].sound);
6685 static char buf[MSG_SIZ];
6686 DWORD bufsiz = MSG_SIZ;
6688 if (!GetUserName(buf, &bufsiz)) {
6689 /*DisplayError("Error getting user name", GetLastError());*/
6690 strcpy(buf, "User");
6698 static char buf[MSG_SIZ];
6699 DWORD bufsiz = MSG_SIZ;
6701 if (!GetComputerName(buf, &bufsiz)) {
6702 /*DisplayError("Error getting host name", GetLastError());*/
6703 strcpy(buf, "Unknown");
6712 return clockTimerEvent != 0;
6718 if (clockTimerEvent == 0) return FALSE;
6719 KillTimer(hwndMain, clockTimerEvent);
6720 clockTimerEvent = 0;
6725 StartClockTimer(long millisec)
6727 clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
6728 (UINT) millisec, NULL);
6732 DisplayWhiteClock(long timeRemaining, int highlight)
6735 hdc = GetDC(hwndMain);
6736 if (!IsIconic(hwndMain)) {
6737 DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");
6739 if (highlight && iconCurrent == iconBlack) {
6740 iconCurrent = iconWhite;
6741 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6742 if (IsIconic(hwndMain)) {
6743 DrawIcon(hdc, 2, 2, iconCurrent);
6746 (void) ReleaseDC(hwndMain, hdc);
6748 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6752 DisplayBlackClock(long timeRemaining, int highlight)
6755 hdc = GetDC(hwndMain);
6756 if (!IsIconic(hwndMain)) {
6757 DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");
6759 if (highlight && iconCurrent == iconWhite) {
6760 iconCurrent = iconBlack;
6761 PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6762 if (IsIconic(hwndMain)) {
6763 DrawIcon(hdc, 2, 2, iconCurrent);
6766 (void) ReleaseDC(hwndMain, hdc);
6768 PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6773 LoadGameTimerRunning()
6775 return loadGameTimerEvent != 0;
6781 if (loadGameTimerEvent == 0) return FALSE;
6782 KillTimer(hwndMain, loadGameTimerEvent);
6783 loadGameTimerEvent = 0;
6788 StartLoadGameTimer(long millisec)
6790 loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
6791 (UINT) millisec, NULL);
6799 char fileTitle[MSG_SIZ];
6801 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
6802 f = OpenFileDialog(hwndMain, TRUE, defName,
6803 appData.oldSaveStyle ? "gam" : "pgn",
6805 "Save Game to File", NULL, fileTitle, NULL);
6814 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
6816 if (delayedTimerEvent != 0) {
6817 if (appData.debugMode) {
6818 fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
6820 KillTimer(hwndMain, delayedTimerEvent);
6821 delayedTimerEvent = 0;
6822 delayedTimerCallback();
6824 delayedTimerCallback = cb;
6825 delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
6826 (UINT) millisec, NULL);
6829 DelayedEventCallback
6832 if (delayedTimerEvent) {
6833 return delayedTimerCallback;
6840 CancelDelayedEvent()
6842 if (delayedTimerEvent) {
6843 KillTimer(hwndMain, delayedTimerEvent);
6844 delayedTimerEvent = 0;
6848 /* Start a child process running the given program.
6849 The process's standard output can be read from "from", and its
6850 standard input can be written to "to".
6851 Exit with fatal error if anything goes wrong.
6852 Returns an opaque pointer that can be used to destroy the process
6856 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
6858 #define BUFSIZE 4096
6860 HANDLE hChildStdinRd, hChildStdinWr,
6861 hChildStdoutRd, hChildStdoutWr;
6862 HANDLE hChildStdinWrDup, hChildStdoutRdDup;
6863 SECURITY_ATTRIBUTES saAttr;
6865 PROCESS_INFORMATION piProcInfo;
6866 STARTUPINFO siStartInfo;
6871 if (appData.debugMode) {
6872 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
6877 /* Set the bInheritHandle flag so pipe handles are inherited. */
6878 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
6879 saAttr.bInheritHandle = TRUE;
6880 saAttr.lpSecurityDescriptor = NULL;
6883 * The steps for redirecting child's STDOUT:
6884 * 1. Create anonymous pipe to be STDOUT for child.
6885 * 2. Create a noninheritable duplicate of read handle,
6886 * and close the inheritable read handle.
6889 /* Create a pipe for the child's STDOUT. */
6890 if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
6891 return GetLastError();
6894 /* Duplicate the read handle to the pipe, so it is not inherited. */
6895 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
6896 GetCurrentProcess(), &hChildStdoutRdDup, 0,
6897 FALSE, /* not inherited */
6898 DUPLICATE_SAME_ACCESS);
6900 return GetLastError();
6902 CloseHandle(hChildStdoutRd);
6905 * The steps for redirecting child's STDIN:
6906 * 1. Create anonymous pipe to be STDIN for child.
6907 * 2. Create a noninheritable duplicate of write handle,
6908 * and close the inheritable write handle.
6911 /* Create a pipe for the child's STDIN. */
6912 if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
6913 return GetLastError();
6916 /* Duplicate the write handle to the pipe, so it is not inherited. */
6917 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
6918 GetCurrentProcess(), &hChildStdinWrDup, 0,
6919 FALSE, /* not inherited */
6920 DUPLICATE_SAME_ACCESS);
6922 return GetLastError();
6924 CloseHandle(hChildStdinWr);
6926 /* Arrange to (1) look in dir for the child .exe file, and
6927 * (2) have dir be the child's working directory. Interpret
6928 * dir relative to the directory WinBoard loaded from. */
6929 GetCurrentDirectory(MSG_SIZ, buf);
6930 SetCurrentDirectory(installDir);
6931 SetCurrentDirectory(dir);
6933 /* Now create the child process. */
6935 siStartInfo.cb = sizeof(STARTUPINFO);
6936 siStartInfo.lpReserved = NULL;
6937 siStartInfo.lpDesktop = NULL;
6938 siStartInfo.lpTitle = NULL;
6939 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
6940 siStartInfo.cbReserved2 = 0;
6941 siStartInfo.lpReserved2 = NULL;
6942 siStartInfo.hStdInput = hChildStdinRd;
6943 siStartInfo.hStdOutput = hChildStdoutWr;
6944 siStartInfo.hStdError = hChildStdoutWr;
6946 fSuccess = CreateProcess(NULL,
6947 cmdLine, /* command line */
6948 NULL, /* process security attributes */
6949 NULL, /* primary thread security attrs */
6950 TRUE, /* handles are inherited */
6951 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
6952 NULL, /* use parent's environment */
6954 &siStartInfo, /* STARTUPINFO pointer */
6955 &piProcInfo); /* receives PROCESS_INFORMATION */
6957 err = GetLastError();
6958 SetCurrentDirectory(buf); /* return to prev directory */
6963 /* Close the handles we don't need in the parent */
6964 CloseHandle(piProcInfo.hThread);
6965 CloseHandle(hChildStdinRd);
6966 CloseHandle(hChildStdoutWr);
6968 /* Prepare return value */
6969 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6971 cp->hProcess = piProcInfo.hProcess;
6972 cp->pid = piProcInfo.dwProcessId;
6973 cp->hFrom = hChildStdoutRdDup;
6974 cp->hTo = hChildStdinWrDup;
6978 /* Klaus Friedel says that this Sleep solves a problem under Windows
6979 2000 where engines sometimes don't see the initial command(s)
6980 from WinBoard and hang. I don't understand how that can happen,
6981 but the Sleep is harmless, so I've put it in. Others have also
6982 reported what may be the same problem, so hopefully this will fix
6991 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
6995 cp = (ChildProc *) pr;
6996 if (cp == NULL) return;
7000 /* TerminateProcess is considered harmful, so... */
7001 CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
7002 if (cp->hFrom) CloseHandle(cp->hFrom); /* if NULL, InputThread will close it */
7003 /* The following doesn't work because the chess program
7004 doesn't "have the same console" as WinBoard. Maybe
7005 we could arrange for this even though neither WinBoard
7006 nor the chess program uses a console for stdio? */
7007 /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
7008 CloseHandle(cp->hProcess);
7012 if (cp->hFrom) CloseHandle(cp->hFrom);
7016 closesocket(cp->sock);
7021 if (signal) send(cp->sock2, "\017", 1, 0); /* 017 = 15 = SIGTERM */
7022 closesocket(cp->sock);
7023 closesocket(cp->sock2);
7031 InterruptChildProcess(ProcRef pr)
7035 cp = (ChildProc *) pr;
7036 if (cp == NULL) return;
7039 /* The following doesn't work because the chess program
7040 doesn't "have the same console" as WinBoard. Maybe
7041 we could arrange for this even though neither WinBoard
7042 nor the chess program uses a console for stdio */
7043 /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
7048 /* Can't interrupt */
7052 send(cp->sock2, "\002", 1, 0); /* 2 = SIGINT */
7059 OpenTelnet(char *host, char *port, ProcRef *pr)
7061 char cmdLine[MSG_SIZ];
7063 if (port[0] == NULLCHAR) {
7064 sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
7066 sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
7068 return StartChildProcess(cmdLine, "", pr);
7072 /* Code to open TCP sockets */
7075 OpenTCP(char *host, char *port, ProcRef *pr)
7080 struct sockaddr_in sa, mysa;
7081 struct hostent FAR *hp;
7082 unsigned short uport;
7083 WORD wVersionRequested;
7086 /* Initialize socket DLL */
7087 wVersionRequested = MAKEWORD(1, 1);
7088 err = WSAStartup(wVersionRequested, &wsaData);
7089 if (err != 0) return err;
7092 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7093 err = WSAGetLastError();
7098 /* Bind local address using (mostly) don't-care values.
7100 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7101 mysa.sin_family = AF_INET;
7102 mysa.sin_addr.s_addr = INADDR_ANY;
7103 uport = (unsigned short) 0;
7104 mysa.sin_port = htons(uport);
7105 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7107 err = WSAGetLastError();
7112 /* Resolve remote host name */
7113 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7114 if (!(hp = gethostbyname(host))) {
7115 unsigned int b0, b1, b2, b3;
7117 err = WSAGetLastError();
7119 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7120 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7121 hp->h_addrtype = AF_INET;
7123 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7124 hp->h_addr_list[0] = (char *) malloc(4);
7125 hp->h_addr_list[0][0] = (char) b0;
7126 hp->h_addr_list[0][1] = (char) b1;
7127 hp->h_addr_list[0][2] = (char) b2;
7128 hp->h_addr_list[0][3] = (char) b3;
7134 sa.sin_family = hp->h_addrtype;
7135 uport = (unsigned short) atoi(port);
7136 sa.sin_port = htons(uport);
7137 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7139 /* Make connection */
7140 if (connect(s, (struct sockaddr *) &sa,
7141 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7142 err = WSAGetLastError();
7147 /* Prepare return value */
7148 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7151 *pr = (ProcRef *) cp;
7157 OpenCommPort(char *name, ProcRef *pr)
7162 char fullname[MSG_SIZ];
7165 sprintf(fullname, "\\\\.\\%s", name);
7167 strcpy(fullname, name);
7169 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
7170 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
7171 if (h == (HANDLE) -1) {
7172 return GetLastError();
7176 if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
7178 /* Accumulate characters until a 100ms pause, then parse */
7179 ct.ReadIntervalTimeout = 100;
7180 ct.ReadTotalTimeoutMultiplier = 0;
7181 ct.ReadTotalTimeoutConstant = 0;
7182 ct.WriteTotalTimeoutMultiplier = 0;
7183 ct.WriteTotalTimeoutConstant = 0;
7184 if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
7186 /* Prepare return value */
7187 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7191 *pr = (ProcRef *) cp;
7197 OpenLoopback(ProcRef *pr)
7199 DisplayFatalError("Not implemented", 0, 1);
7205 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
7210 struct sockaddr_in sa, mysa;
7211 struct hostent FAR *hp;
7212 unsigned short uport;
7213 WORD wVersionRequested;
7216 char stderrPortStr[MSG_SIZ];
7218 /* Initialize socket DLL */
7219 wVersionRequested = MAKEWORD(1, 1);
7220 err = WSAStartup(wVersionRequested, &wsaData);
7221 if (err != 0) return err;
7223 /* Resolve remote host name */
7224 memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7225 if (!(hp = gethostbyname(host))) {
7226 unsigned int b0, b1, b2, b3;
7228 err = WSAGetLastError();
7230 if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7231 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7232 hp->h_addrtype = AF_INET;
7234 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7235 hp->h_addr_list[0] = (char *) malloc(4);
7236 hp->h_addr_list[0][0] = (char) b0;
7237 hp->h_addr_list[0][1] = (char) b1;
7238 hp->h_addr_list[0][2] = (char) b2;
7239 hp->h_addr_list[0][3] = (char) b3;
7245 sa.sin_family = hp->h_addrtype;
7246 uport = (unsigned short) 514;
7247 sa.sin_port = htons(uport);
7248 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7250 /* Bind local socket to unused "privileged" port address
7253 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7254 mysa.sin_family = AF_INET;
7255 mysa.sin_addr.s_addr = INADDR_ANY;
7256 for (fromPort = 1023;; fromPort--) {
7259 return WSAEADDRINUSE;
7261 if (s == INVALID_SOCKET) {
7262 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7263 err = WSAGetLastError();
7268 uport = (unsigned short) fromPort;
7269 mysa.sin_port = htons(uport);
7270 if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7272 err = WSAGetLastError();
7273 if (err == WSAEADDRINUSE) continue;
7277 if (connect(s, (struct sockaddr *) &sa,
7278 sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7279 err = WSAGetLastError();
7280 if (err == WSAEADDRINUSE) {
7291 /* Bind stderr local socket to unused "privileged" port address
7293 s2 = INVALID_SOCKET;
7294 memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7295 mysa.sin_family = AF_INET;
7296 mysa.sin_addr.s_addr = INADDR_ANY;
7297 for (fromPort = 1023;; fromPort--) {
7298 if (fromPort == prevStderrPort) continue; // don't reuse port
7300 (void) closesocket(s);
7302 return WSAEADDRINUSE;
7304 if (s2 == INVALID_SOCKET) {
7305 if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7306 err = WSAGetLastError();
7312 uport = (unsigned short) fromPort;
7313 mysa.sin_port = htons(uport);
7314 if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7316 err = WSAGetLastError();
7317 if (err == WSAEADDRINUSE) continue;
7318 (void) closesocket(s);
7322 if (listen(s2, 1) == SOCKET_ERROR) {
7323 err = WSAGetLastError();
7324 if (err == WSAEADDRINUSE) {
7326 s2 = INVALID_SOCKET;
7329 (void) closesocket(s);
7330 (void) closesocket(s2);
7336 prevStderrPort = fromPort; // remember port used
7337 sprintf(stderrPortStr, "%d", fromPort);
7339 if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
7340 err = WSAGetLastError();
7341 (void) closesocket(s);
7342 (void) closesocket(s2);
7347 if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
7348 err = WSAGetLastError();
7349 (void) closesocket(s);
7350 (void) closesocket(s2);
7354 if (*user == NULLCHAR) user = UserName();
7355 if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
7356 err = WSAGetLastError();
7357 (void) closesocket(s);
7358 (void) closesocket(s2);
7362 if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
7363 err = WSAGetLastError();
7364 (void) closesocket(s);
7365 (void) closesocket(s2);
7370 if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
7371 err = WSAGetLastError();
7372 (void) closesocket(s);
7373 (void) closesocket(s2);
7377 (void) closesocket(s2); /* Stop listening */
7379 /* Prepare return value */
7380 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7384 *pr = (ProcRef *) cp;
7391 AddInputSource(ProcRef pr, int lineByLine,
7392 InputCallback func, VOIDSTAR closure)
7394 InputSource *is, *is2;
7395 ChildProc *cp = (ChildProc *) pr;
7397 is = (InputSource *) calloc(1, sizeof(InputSource));
7398 is->lineByLine = lineByLine;
7400 is->closure = closure;
7405 consoleInputSource = is;
7407 is->kind = cp->kind;
7410 is->hFile = cp->hFrom;
7411 cp->hFrom = NULL; /* now owned by InputThread */
7413 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
7414 (LPVOID) is, 0, &is->id);
7418 is->hFile = cp->hFrom;
7419 cp->hFrom = NULL; /* now owned by InputThread */
7421 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
7422 (LPVOID) is, 0, &is->id);
7426 is->sock = cp->sock;
7428 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7429 (LPVOID) is, 0, &is->id);
7433 is2 = (InputSource *) calloc(1, sizeof(InputSource));
7435 is->sock = cp->sock;
7437 is2->sock = cp->sock2;
7440 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7441 (LPVOID) is, 0, &is->id);
7443 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7444 (LPVOID) is2, 0, &is2->id);
7448 return (InputSourceRef) is;
7452 RemoveInputSource(InputSourceRef isr)
7456 is = (InputSource *) isr;
7457 is->hThread = NULL; /* tell thread to stop */
7458 CloseHandle(is->hThread);
7459 if (is->second != NULL) {
7460 is->second->hThread = NULL;
7461 CloseHandle(is->second->hThread);
7467 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
7470 int outCount = SOCKET_ERROR;
7471 ChildProc *cp = (ChildProc *) pr;
7472 static OVERLAPPED ovl;
7475 ConsoleOutput(message, count, FALSE);
7479 if (ovl.hEvent == NULL) {
7480 ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
7482 ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
7487 outCount = send(cp->sock, message, count, 0);
7488 if (outCount == SOCKET_ERROR) {
7489 *outError = WSAGetLastError();
7491 *outError = NO_ERROR;
7496 if (WriteFile(((ChildProc *)pr)->hTo, message, count,
7497 &dOutCount, NULL)) {
7498 *outError = NO_ERROR;
7499 outCount = (int) dOutCount;
7501 *outError = GetLastError();
7506 *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
7508 if (*outError == NO_ERROR) {
7509 outCount = (int) dOutCount;
7517 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
7520 /* Ignore delay, not implemented for WinBoard */
7521 return OutputToProcess(pr, message, count, outError);
7526 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
7527 char *buf, int count, int error)
7529 DisplayFatalError("Not implemented", 0, 1);
7532 /* see wgamelist.c for Game List functions */
7533 /* see wedittags.c for Edit Tags functions */
7543 if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
7544 f = fopen(buf, "r");
7546 ProcessICSInitScript(f);
7554 StartAnalysisClock()
7556 if (analysisTimerEvent) return;
7557 analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
7562 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
7564 static HANDLE hwndText;
7566 static int sizeX, sizeY;
7567 int newSizeX, newSizeY, flags;
7571 case WM_INITDIALOG: /* message: initialize dialog box */
7572 /* Initialize the dialog items */
7573 hwndText = GetDlgItem(hDlg, OPT_AnalysisText);
7574 SetWindowText(hDlg, analysisTitle);
7575 SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);
7576 /* Size and position the dialog */
7577 if (!analysisDialog) {
7578 analysisDialog = hDlg;
7579 flags = SWP_NOZORDER;
7580 GetClientRect(hDlg, &rect);
7582 sizeY = rect.bottom;
7583 if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&
7584 analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {
7586 EnsureOnScreen(&analysisX, &analysisY);
7587 wp.length = sizeof(WINDOWPLACEMENT);
7589 wp.showCmd = SW_SHOW;
7590 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
7591 wp.rcNormalPosition.left = analysisX;
7592 wp.rcNormalPosition.right = analysisX + analysisW;
7593 wp.rcNormalPosition.top = analysisY;
7594 wp.rcNormalPosition.bottom = analysisY + analysisH;
7595 SetWindowPlacement(hDlg, &wp);
7597 GetClientRect(hDlg, &rect);
7598 newSizeX = rect.right;
7599 newSizeY = rect.bottom;
7600 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
7601 newSizeX, newSizeY);
7608 case WM_COMMAND: /* message: received a command */
7609 switch (LOWORD(wParam)) {
7619 newSizeX = LOWORD(lParam);
7620 newSizeY = HIWORD(lParam);
7621 ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
7626 case WM_GETMINMAXINFO:
7627 /* Prevent resizing window too small */
7628 mmi = (MINMAXINFO *) lParam;
7629 mmi->ptMinTrackSize.x = 100;
7630 mmi->ptMinTrackSize.y = 100;
7637 AnalysisPopUp(char* title, char* str)
7642 if (str == NULL) str = "";
7643 p = (char *) malloc(2 * strlen(str) + 2);
7646 if (*str == '\n') *q++ = '\r';
7650 if (analysisText != NULL) free(analysisText);
7653 if (analysisDialog) {
7654 SetWindowText(analysisDialog, title);
7655 SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);
7656 ShowWindow(analysisDialog, SW_SHOW);
7658 analysisTitle = title;
7659 lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);
7660 CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),
7661 hwndMain, (DLGPROC)lpProc);
7662 FreeProcInstance(lpProc);
7664 analysisDialogUp = TRUE;
7670 if (analysisDialog) {
7671 ShowWindow(analysisDialog, SW_HIDE);
7673 analysisDialogUp = FALSE;
7678 SetHighlights(int fromX, int fromY, int toX, int toY)
7680 highlightInfo.sq[0].x = fromX;
7681 highlightInfo.sq[0].y = fromY;
7682 highlightInfo.sq[1].x = toX;
7683 highlightInfo.sq[1].y = toY;
7689 highlightInfo.sq[0].x = highlightInfo.sq[0].y =
7690 highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
7694 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
7696 premoveHighlightInfo.sq[0].x = fromX;
7697 premoveHighlightInfo.sq[0].y = fromY;
7698 premoveHighlightInfo.sq[1].x = toX;
7699 premoveHighlightInfo.sq[1].y = toY;
7703 ClearPremoveHighlights()
7705 premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y =
7706 premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
7712 if (saveSettingsOnExit) SaveSettings(settingsFileName);
7713 DeleteClipboardTempFiles();
7719 if (IsIconic(hwndMain))
7720 ShowWindow(hwndMain, SW_RESTORE);
7722 SetActiveWindow(hwndMain);
7726 * Prototypes for animation support routines
7728 static void ScreenSquare(int column, int row, POINT * pt);
7729 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
7730 POINT frames[], int * nFrames);
7736 AnimateMove(board, fromX, fromY, toX, toY)
7744 POINT start, finish, mid;
7745 POINT frames[kFactor * 2 + 1];
7748 if (!appData.animate) return;
7749 if (doingSizing) return;
7750 if (fromY < 0 || fromX < 0) return;
7751 piece = board[fromY][fromX];
7752 if (piece >= EmptySquare) return;
7754 ScreenSquare(fromX, fromY, &start);
7755 ScreenSquare(toX, toY, &finish);
7757 /* All pieces except knights move in straight line */
7758 if (piece != WhiteKnight && piece != BlackKnight) {
7759 mid.x = start.x + (finish.x - start.x) / 2;
7760 mid.y = start.y + (finish.y - start.y) / 2;
7762 /* Knight: make diagonal movement then straight */
7763 if (abs(toY - fromY) < abs(toX - fromX)) {
7764 mid.x = start.x + (finish.x - start.x) / 2;
7768 mid.y = start.y + (finish.y - start.y) / 2;
7772 /* Don't use as many frames for very short moves */
7773 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7774 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7776 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7778 animInfo.from.x = fromX;
7779 animInfo.from.y = fromY;
7780 animInfo.to.x = toX;
7781 animInfo.to.y = toY;
7782 animInfo.lastpos = start;
7783 animInfo.piece = piece;
7784 for (n = 0; n < nFrames; n++) {
7785 animInfo.pos = frames[n];
7786 DrawPosition(FALSE, NULL);
7787 animInfo.lastpos = animInfo.pos;
7788 Sleep(appData.animSpeed);
7790 animInfo.pos = finish;
7791 DrawPosition(FALSE, NULL);
7792 animInfo.piece = EmptySquare;
7795 /* Convert board position to corner of screen rect and color */
7798 ScreenSquare(column, row, pt)
7799 int column; int row; POINT * pt;
7802 pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
7803 pt->y = lineGap + row * (squareSize + lineGap);
7805 pt->x = lineGap + column * (squareSize + lineGap);
7806 pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
7810 /* Generate a series of frame coords from start->mid->finish.
7811 The movement rate doubles until the half way point is
7812 reached, then halves back down to the final destination,
7813 which gives a nice slow in/out effect. The algorithmn
7814 may seem to generate too many intermediates for short
7815 moves, but remember that the purpose is to attract the
7816 viewers attention to the piece about to be moved and
7817 then to where it ends up. Too few frames would be less
7821 Tween(start, mid, finish, factor, frames, nFrames)
7822 POINT * start; POINT * mid;
7823 POINT * finish; int factor;
7824 POINT frames[]; int * nFrames;
7826 int n, fraction = 1, count = 0;
7828 /* Slow in, stepping 1/16th, then 1/8th, ... */
7829 for (n = 0; n < factor; n++)
7831 for (n = 0; n < factor; n++) {
7832 frames[count].x = start->x + (mid->x - start->x) / fraction;
7833 frames[count].y = start->y + (mid->y - start->y) / fraction;
7835 fraction = fraction / 2;
7839 frames[count] = *mid;
7842 /* Slow out, stepping 1/2, then 1/4, ... */
7844 for (n = 0; n < factor; n++) {
7845 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
7846 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
7848 fraction = fraction * 2;
7854 HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
7856 /* Currently not implemented in WinBoard */