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 dcb.DCBlength = sizeof(DCB);
1615 dcb.BaudRate = 9600;
1617 dcb.fParity = FALSE;
1618 dcb.fOutxCtsFlow = FALSE;
1619 dcb.fOutxDsrFlow = FALSE;
1620 dcb.fDtrControl = DTR_CONTROL_ENABLE;
1621 dcb.fDsrSensitivity = FALSE;
1622 dcb.fTXContinueOnXoff = TRUE;
1626 dcb.fRtsControl = RTS_CONTROL_ENABLE;
1627 dcb.fAbortOnError = FALSE;
1628 /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */
1629 #if (defined(_MSC_VER) && _MSC_VER <= 1200)
1635 dcb.Parity = SPACEPARITY;
1636 dcb.StopBits = ONESTOPBIT;
1637 settingsFileName = SETTINGS_FILE;
1638 saveSettingsOnExit = TRUE;
1639 boardX = CW_USEDEFAULT;
1640 boardY = CW_USEDEFAULT;
1641 consoleX = CW_USEDEFAULT;
1642 consoleY = CW_USEDEFAULT;
1643 consoleW = CW_USEDEFAULT;
1644 consoleH = CW_USEDEFAULT;
1645 analysisX = CW_USEDEFAULT;
1646 analysisY = CW_USEDEFAULT;
1647 analysisW = CW_USEDEFAULT;
1648 analysisH = CW_USEDEFAULT;
1649 commentX = CW_USEDEFAULT;
1650 commentY = CW_USEDEFAULT;
1651 commentW = CW_USEDEFAULT;
1652 commentH = CW_USEDEFAULT;
1653 editTagsX = CW_USEDEFAULT;
1654 editTagsY = CW_USEDEFAULT;
1655 editTagsW = CW_USEDEFAULT;
1656 editTagsH = CW_USEDEFAULT;
1657 gameListX = CW_USEDEFAULT;
1658 gameListY = CW_USEDEFAULT;
1659 gameListW = CW_USEDEFAULT;
1660 gameListH = CW_USEDEFAULT;
1661 icsTextMenuString = ICS_TEXT_MENU_DEFAULT;
1662 icsNames = ICS_NAMES;
1663 firstChessProgramNames = FCP_NAMES;
1664 secondChessProgramNames = SCP_NAMES;
1665 appData.initialMode = "";
1666 appData.variant = "normal";
1667 appData.firstProtocolVersion = PROTOVER;
1668 appData.secondProtocolVersion = PROTOVER;
1669 appData.showButtonBar = TRUE;
1671 appData.zippyTalk = ZIPPY_TALK;
1672 appData.zippyPlay = ZIPPY_PLAY;
1673 appData.zippyLines = ZIPPY_LINES;
1674 appData.zippyPinhead = ZIPPY_PINHEAD;
1675 appData.zippyPassword = ZIPPY_PASSWORD;
1676 appData.zippyPassword2 = ZIPPY_PASSWORD2;
1677 appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;
1678 appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;
1679 appData.zippyUseI = ZIPPY_USE_I;
1680 appData.zippyBughouse = ZIPPY_BUGHOUSE;
1681 appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;
1682 appData.zippyGameEnd = ZIPPY_GAME_END;
1683 appData.zippyGameStart = ZIPPY_GAME_START;
1684 appData.zippyAdjourn = ZIPPY_ADJOURN;
1685 appData.zippyAbort = ZIPPY_ABORT;
1686 appData.zippyVariants = ZIPPY_VARIANTS;
1687 appData.zippyMaxGames = ZIPPY_MAX_GAMES;
1688 appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;
1691 /* Point font array elements to structures and
1692 parse default font names */
1693 for (i=0; i<NUM_FONTS; i++) {
1694 for (j=0; j<NUM_SIZES; j++) {
1695 font[j][i] = &fontRec[j][i];
1696 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
1700 /* Parse default settings file if any */
1701 if (ParseSettingsFile(settingsFileName, buf)) {
1702 settingsFileName = strdup(buf);
1705 /* Parse command line */
1706 ParseArgs(StringGet, &lpCmdLine);
1708 /* Propagate options that affect others */
1709 if (appData.matchMode || appData.matchGames) chessProgram = TRUE;
1710 if (appData.icsActive || appData.noChessProgram) {
1711 chessProgram = FALSE; /* not local chess program mode */
1714 /* Open startup dialog if needed */
1715 if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||
1716 (appData.icsActive && *appData.icsHost == NULLCHAR) ||
1717 (chessProgram && (*appData.firstChessProgram == NULLCHAR ||
1718 *appData.secondChessProgram == NULLCHAR))) {
1721 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
1722 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
1723 FreeProcInstance(lpProc);
1726 /* Make sure save files land in the right (?) directory */
1727 if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {
1728 appData.saveGameFile = strdup(buf);
1730 if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {
1731 appData.savePositionFile = strdup(buf);
1734 /* Finish initialization for fonts and sounds */
1735 for (i=0; i<NUM_FONTS; i++) {
1736 for (j=0; j<NUM_SIZES; j++) {
1737 CreateFontInMF(font[j][i]);
1740 /* xboard, and older WinBoards, controlled the move sound with the
1741 appData.ringBellAfterMoves option. In the current WinBoard, we
1742 always turn the option on (so that the backend will call us),
1743 then let the user turn the sound off by setting it to silence if
1744 desired. To accommodate old winboard.ini files saved by old
1745 versions of WinBoard, we also turn off the sound if the option
1746 was initially set to false. */
1747 if (!appData.ringBellAfterMoves) {
1748 sounds[(int)SoundMove].name = strdup("");
1749 appData.ringBellAfterMoves = TRUE;
1751 GetCurrentDirectory(MSG_SIZ, currDir);
1752 SetCurrentDirectory(installDir);
1754 SetCurrentDirectory(currDir);
1756 p = icsTextMenuString;
1758 FILE* f = fopen(p + 1, "r");
1760 DisplayFatalError(p + 1, errno, 2);
1763 i = fread(buf, 1, sizeof(buf)-1, f);
1768 ParseIcsTextMenu(strdup(p));
1775 HMENU hmenu = GetMenu(hwndMain);
1777 (void) EnableMenuItem(hmenu, IDM_CommPort,
1778 MF_BYCOMMAND|((appData.icsActive &&
1779 *appData.icsCommPort != NULLCHAR) ?
1780 MF_ENABLED : MF_GRAYED));
1781 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
1782 MF_BYCOMMAND|(saveSettingsOnExit ?
1783 MF_CHECKED : MF_UNCHECKED));
1788 SaveSettings(char* name)
1795 if (!hwndMain) return;
1797 GetCurrentDirectory(MSG_SIZ, dir);
1798 SetCurrentDirectory(installDir);
1799 f = fopen(name, "w");
1800 SetCurrentDirectory(dir);
1802 DisplayError(name, errno);
1806 fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);
1808 fprintf(f, "; You can edit the values of options that are already set in this file,\n");
1809 fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");
1810 fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");
1813 wp.length = sizeof(WINDOWPLACEMENT);
1814 GetWindowPlacement(hwndMain, &wp);
1815 boardX = wp.rcNormalPosition.left;
1816 boardY = wp.rcNormalPosition.top;
1819 GetWindowPlacement(hwndConsole, &wp);
1820 consoleX = wp.rcNormalPosition.left;
1821 consoleY = wp.rcNormalPosition.top;
1822 consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1823 consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1826 if (analysisDialog) {
1827 GetWindowPlacement(analysisDialog, &wp);
1828 analysisX = wp.rcNormalPosition.left;
1829 analysisY = wp.rcNormalPosition.top;
1830 analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1831 analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1834 if (commentDialog) {
1835 GetWindowPlacement(commentDialog, &wp);
1836 commentX = wp.rcNormalPosition.left;
1837 commentY = wp.rcNormalPosition.top;
1838 commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1839 commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1842 if (editTagsDialog) {
1843 GetWindowPlacement(editTagsDialog, &wp);
1844 editTagsX = wp.rcNormalPosition.left;
1845 editTagsY = wp.rcNormalPosition.top;
1846 editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1847 editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1850 if (gameListDialog) {
1851 GetWindowPlacement(gameListDialog, &wp);
1852 gameListX = wp.rcNormalPosition.left;
1853 gameListY = wp.rcNormalPosition.top;
1854 gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1855 gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1858 for (ad = argDescriptors; ad->argName != NULL; ad++) {
1859 if (!ad->save) continue;
1860 switch (ad->argType) {
1863 char *p = *(char **)ad->argLoc;
1864 if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {
1865 /* Quote multiline values or \-containing values
1866 with { } if possible */
1867 fprintf(f, "/%s={%s}\n", ad->argName, p);
1869 /* Else quote with " " */
1870 fprintf(f, "/%s=\"", ad->argName);
1872 if (*p == '\n') fprintf(f, "\n");
1873 else if (*p == '\r') fprintf(f, "\\r");
1874 else if (*p == '\t') fprintf(f, "\\t");
1875 else if (*p == '\b') fprintf(f, "\\b");
1876 else if (*p == '\f') fprintf(f, "\\f");
1877 else if (*p < ' ') fprintf(f, "\\%03o", *p);
1878 else if (*p == '\"') fprintf(f, "\\\"");
1879 else if (*p == '\\') fprintf(f, "\\\\");
1888 fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);
1891 fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);
1894 fprintf(f, "/%s=%s\n", ad->argName,
1895 (*(Boolean *)ad->argLoc) ? "true" : "false");
1898 if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1901 if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1905 COLORREF color = *(COLORREF *)ad->argLoc;
1906 fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName,
1907 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1912 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
1913 fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,
1914 (ta->effects & CFE_BOLD) ? "b" : "",
1915 (ta->effects & CFE_ITALIC) ? "i" : "",
1916 (ta->effects & CFE_UNDERLINE) ? "u" : "",
1917 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
1918 (ta->effects) ? " " : "",
1919 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
1923 if (strchr(*(char **)ad->argLoc, '\"')) {
1924 fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);
1926 fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);
1930 fprintf(f, "/%s=%s\n", ad->argName,
1931 sizeInfo[*(BoardSize *)ad->argLoc].name);
1936 for (bs=0; bs<NUM_SIZES; bs++) {
1937 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
1938 fprintf(f, "/size=%s ", sizeInfo[bs].name);
1939 fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",
1940 ad->argName, mfp->faceName, mfp->pointSize,
1941 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
1942 mfp->bold ? "b" : "",
1943 mfp->italic ? "i" : "",
1944 mfp->underline ? "u" : "",
1945 mfp->strikeout ? "s" : "");
1949 case ArgCommSettings:
1950 PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);
1958 /*---------------------------------------------------------------------------*\
1960 * GDI board drawing routines
1962 \*---------------------------------------------------------------------------*/
1965 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
1969 sprintf(name, "%s%d%s", piece, squareSize, suffix);
1970 if (gameInfo.event &&
1971 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
1972 strcmp(name, "k80s") == 0) {
1973 strcpy(name, "tim");
1975 return LoadBitmap(hinst, name);
1979 /* Insert a color into the program's logical palette
1980 structure. This code assumes the given color is
1981 the result of the RGB or PALETTERGB macro, and it
1982 knows how those macros work (which is documented).
1985 InsertInPalette(COLORREF color)
1987 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
1989 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
1990 DisplayFatalError("Too many colors", 0, 1);
1991 pLogPal->palNumEntries--;
1995 pe->peFlags = (char) 0;
1996 pe->peRed = (char) (0xFF & color);
1997 pe->peGreen = (char) (0xFF & (color >> 8));
1998 pe->peBlue = (char) (0xFF & (color >> 16));
2006 if (pLogPal == NULL) {
2007 /* Allocate enough memory for a logical palette with
2008 * PALETTESIZE entries and set the size and version fields
2009 * of the logical palette structure.
2011 pLogPal = (NPLOGPALETTE)
2012 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
2013 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
2014 pLogPal->palVersion = 0x300;
2016 pLogPal->palNumEntries = 0;
2018 InsertInPalette(lightSquareColor);
2019 InsertInPalette(darkSquareColor);
2020 InsertInPalette(whitePieceColor);
2021 InsertInPalette(blackPieceColor);
2022 InsertInPalette(highlightSquareColor);
2023 InsertInPalette(premoveHighlightColor);
2025 /* create a logical color palette according the information
2026 * in the LOGPALETTE structure.
2028 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
2030 lightSquareBrush = CreateSolidBrush(lightSquareColor);
2031 darkSquareBrush = CreateSolidBrush(darkSquareColor);
2032 whitePieceBrush = CreateSolidBrush(whitePieceColor);
2033 blackPieceBrush = CreateSolidBrush(blackPieceColor);
2034 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
2039 BoardWidth(int boardSize)
2041 return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +
2042 BOARD_SIZE * sizeInfo[boardSize].squareSize;
2045 /* Respond to board resize by dragging edge */
2047 ResizeBoard(int newSizeX, int newSizeY, int flags)
2049 BoardSize newSize = NUM_SIZES - 1;
2050 static int recurse = 0;
2051 if (IsIconic(hwndMain)) return;
2052 if (recurse > 0) return;
2054 while (newSize > 0 &&
2055 (newSizeX < sizeInfo[newSize].cliWidth ||
2056 newSizeY < sizeInfo[newSize].cliHeight)) {
2059 boardSize = newSize;
2060 InitDrawingSizes(boardSize, flags);
2067 InitDrawingSizes(BoardSize boardSize, int flags)
2071 static int oldBoardSize = -1, oldTinyLayout = 0;
2073 SIZE clockSize, messageSize;
2077 HMENU hmenu = GetMenu(hwndMain);
2082 tinyLayout = sizeInfo[boardSize].tinyLayout;
2083 smallLayout = sizeInfo[boardSize].smallLayout;
2084 squareSize = sizeInfo[boardSize].squareSize;
2085 lineGap = sizeInfo[boardSize].lineGap;
2087 if (tinyLayout != oldTinyLayout) {
2088 long style = GetWindowLong(hwndMain, GWL_STYLE);
2090 style &= ~WS_SYSMENU;
2091 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
2092 "&Minimize\tCtrl+F4");
2094 style |= WS_SYSMENU;
2095 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
2097 SetWindowLong(hwndMain, GWL_STYLE, style);
2099 for (i=0; menuBarText[tinyLayout][i]; i++) {
2100 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
2101 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
2103 DrawMenuBar(hwndMain);
2106 boardWidth = BoardWidth(boardSize);
2108 /* Get text area sizes */
2109 hdc = GetDC(hwndMain);
2110 if (appData.clockMode) {
2111 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
2113 sprintf(buf, "White");
2115 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
2116 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
2117 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2118 str = "We only care about the height here";
2119 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
2120 SelectObject(hdc, oldFont);
2121 ReleaseDC(hwndMain, hdc);
2123 /* Compute where everything goes */
2124 whiteRect.left = OUTER_MARGIN;
2125 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
2126 whiteRect.top = OUTER_MARGIN;
2127 whiteRect.bottom = whiteRect.top + clockSize.cy;
2129 blackRect.left = whiteRect.right + INNER_MARGIN;
2130 blackRect.right = blackRect.left + boardWidth/2 - 1;
2131 blackRect.top = whiteRect.top;
2132 blackRect.bottom = whiteRect.bottom;
2134 messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;
2135 if (appData.showButtonBar) {
2136 messageRect.right = blackRect.right
2137 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
2139 messageRect.right = blackRect.right;
2141 messageRect.top = whiteRect.bottom + INNER_MARGIN;
2142 messageRect.bottom = messageRect.top + messageSize.cy;
2144 boardRect.left = whiteRect.left;
2145 boardRect.right = boardRect.left + boardWidth;
2146 boardRect.top = messageRect.bottom + INNER_MARGIN;
2147 boardRect.bottom = boardRect.top + boardWidth;
2149 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
2150 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
2151 winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
2152 winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
2153 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
2154 GetWindowRect(hwndMain, &wrect);
2155 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2156 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2157 /* compensate if menu bar wrapped */
2158 GetClientRect(hwndMain, &crect);
2159 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
2163 SetWindowPos(hwndMain, NULL,
2164 wrect.right - winWidth, wrect.bottom - winHeight,
2165 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2170 SetWindowPos(hwndMain, NULL,
2171 wrect.left, wrect.bottom - winHeight,
2172 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2175 case WMSZ_BOTTOMLEFT:
2177 SetWindowPos(hwndMain, NULL,
2178 wrect.right - winWidth, wrect.top,
2179 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2182 case WMSZ_BOTTOMRIGHT:
2186 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2187 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2192 for (i = 0; i < N_BUTTONS; i++) {
2193 if (buttonDesc[i].hwnd != NULL) {
2194 DestroyWindow(buttonDesc[i].hwnd);
2195 buttonDesc[i].hwnd = NULL;
2197 if (appData.showButtonBar) {
2198 buttonDesc[i].hwnd =
2199 CreateWindow("BUTTON", buttonDesc[i].label,
2200 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
2201 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
2202 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
2203 (HMENU) buttonDesc[i].id,
2204 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
2206 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
2207 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
2208 MAKELPARAM(FALSE, 0));
2210 if (buttonDesc[i].id == IDM_Pause)
2211 hwndPause = buttonDesc[i].hwnd;
2212 buttonDesc[i].wndproc = (WNDPROC)
2213 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
2216 if (gridPen != NULL) DeleteObject(gridPen);
2217 if (highlightPen != NULL) DeleteObject(highlightPen);
2218 if (premovePen != NULL) DeleteObject(premovePen);
2220 logbrush.lbStyle = BS_SOLID;
2221 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
2223 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2224 lineGap, &logbrush, 0, NULL);
2225 logbrush.lbColor = highlightSquareColor;
2227 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2228 lineGap, &logbrush, 0, NULL);
2230 logbrush.lbColor = premoveHighlightColor;
2232 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2233 lineGap, &logbrush, 0, NULL);
2235 for (i = 0; i < BOARD_SIZE + 1; i++) {
2236 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
2237 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;
2238 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
2239 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
2240 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
2241 BOARD_SIZE * (squareSize + lineGap);
2242 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =
2243 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +
2244 lineGap / 2 + (i * (squareSize + lineGap));
2245 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =
2246 boardRect.top + BOARD_SIZE * (squareSize + lineGap);
2247 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
2251 if (boardSize == oldBoardSize) return;
2252 oldBoardSize = boardSize;
2253 oldTinyLayout = tinyLayout;
2255 /* Load piece bitmaps for this board size */
2256 for (i=0; i<=2; i++) {
2257 for (piece = WhitePawn;
2258 (int) piece <= (int) WhiteKing;
2259 piece = (ChessSquare) ((int) piece + 1)) {
2260 if (pieceBitmap[i][piece] != NULL)
2261 DeleteObject(pieceBitmap[i][piece]);
2265 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
2266 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
2267 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
2268 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
2269 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
2270 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
2271 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
2272 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
2273 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
2274 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
2275 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
2276 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
2277 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
2278 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
2279 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
2280 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
2281 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
2282 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
2287 PieceBitmap(ChessSquare p, int kind)
2289 if ((int) p >= (int) BlackPawn)
2290 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
2292 return pieceBitmap[kind][(int) p];
2295 /***************************************************************/
2297 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
2298 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
2300 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
2301 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
2305 SquareToPos(int row, int column, int * x, int * y)
2308 *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
2309 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
2311 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
2312 *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
2317 DrawCoordsOnDC(HDC hdc)
2319 static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};
2320 static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};
2321 char str[2] = { NULLCHAR, NULLCHAR };
2322 int oldMode, oldAlign, x, y, start, i;
2326 if (!appData.showCoords)
2329 start = flipView ? 0 : 8;
2331 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
2332 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
2333 oldAlign = GetTextAlign(hdc);
2334 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
2336 y = boardRect.top + lineGap;
2337 x = boardRect.left + lineGap;
2339 SetTextAlign(hdc, TA_LEFT|TA_TOP);
2340 for (i = 0; i < 8; i++) {
2341 str[0] = files[start + i];
2342 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
2343 y += squareSize + lineGap;
2346 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
2347 for (i = 0; i < 8; i++) {
2348 str[0] = ranks[start + i];
2349 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
2350 x += squareSize + lineGap;
2353 SelectObject(hdc, oldBrush);
2354 SetBkMode(hdc, oldMode);
2355 SetTextAlign(hdc, oldAlign);
2356 SelectObject(hdc, oldFont);
2360 DrawGridOnDC(HDC hdc)
2365 oldPen = SelectObject(hdc, gridPen);
2366 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);
2367 SelectObject(hdc, oldPen);
2371 #define HIGHLIGHT_PEN 0
2372 #define PREMOVE_PEN 1
2375 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
2379 if (lineGap == 0) return;
2381 x1 = boardRect.left +
2382 lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);
2383 y1 = boardRect.top +
2384 lineGap/2 + y * (squareSize + lineGap);
2386 x1 = boardRect.left +
2387 lineGap/2 + x * (squareSize + lineGap);
2388 y1 = boardRect.top +
2389 lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);
2391 hPen = pen ? premovePen : highlightPen;
2392 oldPen = SelectObject(hdc, on ? hPen : gridPen);
2393 MoveToEx(hdc, x1, y1, NULL);
2394 LineTo(hdc, x1 + squareSize + lineGap, y1);
2395 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
2396 LineTo(hdc, x1, y1 + squareSize + lineGap);
2397 LineTo(hdc, x1, y1);
2398 SelectObject(hdc, oldPen);
2402 DrawHighlightsOnDC(HDC hdc)
2405 for (i=0; i<2; i++) {
2406 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
2407 DrawHighlightOnDC(hdc, TRUE,
2408 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
2411 for (i=0; i<2; i++) {
2412 if (premoveHighlightInfo.sq[i].x >= 0 &&
2413 premoveHighlightInfo.sq[i].y >= 0) {
2414 DrawHighlightOnDC(hdc, TRUE,
2415 premoveHighlightInfo.sq[i].x,
2416 premoveHighlightInfo.sq[i].y,
2422 /* Note: sqcolor is used only in monoMode */
2423 /* Note that this code is largely duplicated in woptions.c,
2424 function DrawSampleSquare, so that needs to be updated too */
2426 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
2431 if (appData.blindfold) return;
2433 if (appData.monoMode) {
2434 SelectObject(tmphdc, PieceBitmap(piece,
2435 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
2436 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
2437 sqcolor ? SRCCOPY : NOTSRCCOPY);
2440 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
2441 oldBrush = SelectObject(hdc, whitePieceBrush);
2442 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2444 /* Use black piece color for outline of white pieces */
2445 /* Not sure this looks really good (though xboard does it).
2446 Maybe better to have another selectable color, default black */
2447 SelectObject(hdc, blackPieceBrush); /* could have own brush */
2448 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2449 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2451 /* Use black for outline of white pieces */
2452 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2453 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);
2457 /* Use white piece color for details of black pieces */
2458 /* Requires filled-in solid bitmaps (BLACK_PIECE class); the
2459 WHITE_PIECE ones aren't always the right shape. */
2460 /* Not sure this looks really good (though xboard does it).
2461 Maybe better to have another selectable color, default medium gray? */
2462 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));
2463 oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */
2464 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2465 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2466 SelectObject(hdc, blackPieceBrush);
2467 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2469 /* Use square color for details of black pieces */
2470 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2471 oldBrush = SelectObject(hdc, blackPieceBrush);
2472 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2475 SelectObject(hdc, oldBrush);
2476 SelectObject(tmphdc, oldBitmap);
2481 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
2483 int row, column, x, y, square_color, piece_color;
2487 for (row = 0; row < BOARD_SIZE; row++) {
2488 for (column = 0; column < BOARD_SIZE; column++) {
2490 SquareToPos(row, column, &x, &y);
2492 piece = board[row][column];
2494 square_color = ((column + row) % 2) == 1;
2495 piece_color = (int) piece < (int) BlackPawn;
2497 if (appData.monoMode) {
2498 if (piece == EmptySquare) {
2499 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
2500 square_color ? WHITENESS : BLACKNESS);
2502 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
2505 oldBrush = SelectObject(hdc, square_color ?
2506 lightSquareBrush : darkSquareBrush);
2507 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
2508 SelectObject(hdc, oldBrush);
2509 if (piece != EmptySquare)
2510 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
2516 #define MAX_CLIPS 200 /* more than enough */
2519 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
2521 static Board lastReq, lastDrawn;
2522 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
2523 static int lastDrawnFlipView = 0;
2524 static int lastReqValid = 0, lastDrawnValid = 0;
2525 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
2528 HBITMAP bufferBitmap;
2531 HRGN clips[MAX_CLIPS];
2532 ChessSquare dragged_piece = EmptySquare;
2534 /* I'm undecided on this - this function figures out whether a full
2535 * repaint is necessary on its own, so there's no real reason to have the
2536 * caller tell it that. I think this can safely be set to FALSE - but
2537 * if we trust the callers not to request full repaints unnessesarily, then
2538 * we could skip some clipping work. In other words, only request a full
2539 * redraw when the majority of pieces have changed positions (ie. flip,
2540 * gamestart and similar) --Hawk
2542 Boolean fullrepaint = repaint;
2544 if (board == NULL) {
2545 if (!lastReqValid) {
2550 CopyBoard(lastReq, board);
2558 if (IsIconic(hwndMain)) {
2563 hdc = GetDC(hwndMain);
2564 if (!appData.monoMode) {
2565 SelectPalette(hdc, hPal, FALSE);
2566 RealizePalette(hdc);
2574 fprintf(debugFP, "*******************************\n"
2576 "dragInfo.from (%d,%d)\n"
2577 "dragInfo.start (%d,%d)\n"
2578 "dragInfo.pos (%d,%d)\n"
2579 "dragInfo.lastpos (%d,%d)\n",
2580 repaint ? "TRUE" : "FALSE",
2581 dragInfo.from.x, dragInfo.from.y,
2582 dragInfo.start.x, dragInfo.start.y,
2583 dragInfo.pos.x, dragInfo.pos.y,
2584 dragInfo.lastpos.x, dragInfo.lastpos.y);
2585 fprintf(debugFP, "prev: ");
2586 for (row = 0; row < 8; row++) {
2587 for (column = 0; column < 8; column++) {
2588 fprintf(debugFP, "%d ", lastDrawn[row][column]);
2591 fprintf(debugFP, "\n");
2592 fprintf(debugFP, "board: ");
2593 for (row = 0; row < 8; row++) {
2594 for (column = 0; column < 8; column++) {
2595 fprintf(debugFP, "%d ", board[row][column]);
2598 fprintf(debugFP, "\n");
2602 /* Create some work-DCs */
2603 hdcmem = CreateCompatibleDC(hdc);
2604 tmphdc = CreateCompatibleDC(hdc);
2606 /* Figure out which squares need updating by comparing the
2607 * newest board with the last drawn board and checking if
2608 * flipping has changed.
2610 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
2611 for (row = 0; row < 8; row++) {
2612 for (column = 0; column < 8; column++) {
2613 if (lastDrawn[row][column] != board[row][column]) {
2614 SquareToPos(row, column, &x, &y);
2615 clips[num_clips++] =
2616 CreateRectRgn(x, y, x + squareSize, y + squareSize);
2620 for (i=0; i<2; i++) {
2621 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
2622 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
2623 if (lastDrawnHighlight.sq[i].x >= 0 &&
2624 lastDrawnHighlight.sq[i].y >= 0) {
2625 SquareToPos(lastDrawnHighlight.sq[i].y,
2626 lastDrawnHighlight.sq[i].x, &x, &y);
2627 clips[num_clips++] =
2628 CreateRectRgn(x - lineGap, y - lineGap,
2629 x + squareSize + lineGap, y + squareSize + lineGap);
2631 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
2632 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
2633 clips[num_clips++] =
2634 CreateRectRgn(x - lineGap, y - lineGap,
2635 x + squareSize + lineGap, y + squareSize + lineGap);
2639 for (i=0; i<2; i++) {
2640 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
2641 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
2642 if (lastDrawnPremove.sq[i].x >= 0 &&
2643 lastDrawnPremove.sq[i].y >= 0) {
2644 SquareToPos(lastDrawnPremove.sq[i].y,
2645 lastDrawnPremove.sq[i].x, &x, &y);
2646 clips[num_clips++] =
2647 CreateRectRgn(x - lineGap, y - lineGap,
2648 x + squareSize + lineGap, y + squareSize + lineGap);
2650 if (premoveHighlightInfo.sq[i].x >= 0 &&
2651 premoveHighlightInfo.sq[i].y >= 0) {
2652 SquareToPos(premoveHighlightInfo.sq[i].y,
2653 premoveHighlightInfo.sq[i].x, &x, &y);
2654 clips[num_clips++] =
2655 CreateRectRgn(x - lineGap, y - lineGap,
2656 x + squareSize + lineGap, y + squareSize + lineGap);
2664 /* Create a buffer bitmap - this is the actual bitmap
2665 * being written to. When all the work is done, we can
2666 * copy it to the real DC (the screen). This avoids
2667 * the problems with flickering.
2669 GetClientRect(hwndMain, &Rect);
2670 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
2671 Rect.bottom-Rect.top+1);
2672 oldBitmap = SelectObject(hdcmem, bufferBitmap);
2673 if (!appData.monoMode) {
2674 SelectPalette(hdcmem, hPal, FALSE);
2677 /* Create clips for dragging */
2679 if (dragInfo.from.x >= 0) {
2680 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
2681 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2683 if (dragInfo.start.x >= 0) {
2684 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
2685 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2687 if (dragInfo.pos.x >= 0) {
2688 x = dragInfo.pos.x - squareSize / 2;
2689 y = dragInfo.pos.y - squareSize / 2;
2690 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2692 if (dragInfo.lastpos.x >= 0) {
2693 x = dragInfo.lastpos.x - squareSize / 2;
2694 y = dragInfo.lastpos.y - squareSize / 2;
2695 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2699 /* If dragging is in progress, we temporarely remove the piece */
2700 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
2701 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
2702 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
2705 /* Are we animating a move?
2707 * - remove the piece from the board (temporarely)
2708 * - calculate the clipping region
2711 if (animInfo.piece != EmptySquare) {
2712 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
2713 x = boardRect.left + animInfo.lastpos.x;
2714 y = boardRect.top + animInfo.lastpos.y;
2715 x2 = boardRect.left + animInfo.pos.x;
2716 y2 = boardRect.top + animInfo.pos.y;
2717 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
2718 /* Slight kludge. The real problem is that after AnimateMove is
2719 done, the position on the screen does not match lastDrawn.
2720 This currently causes trouble only on e.p. captures in
2721 atomic, where the piece moves to an empty square and then
2722 explodes. The old and new positions both had an empty square
2723 at the destination, but animation has drawn a piece there and
2724 we have to remember to erase it. */
2725 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
2729 /* No clips? Make sure we have fullrepaint set to TRUE */
2733 /* Set clipping on the memory DC */
2735 SelectClipRgn(hdcmem, clips[0]);
2736 for (x = 1; x < num_clips; x++) {
2737 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
2738 abort(); // this should never ever happen!
2742 /* Do all the drawing to the memory DC */
2743 DrawGridOnDC(hdcmem);
2744 DrawHighlightsOnDC(hdcmem);
2745 DrawBoardOnDC(hdcmem, board, tmphdc);
2746 DrawCoordsOnDC(hdcmem);
2748 /* Put the dragged piece back into place and draw it */
2749 if (dragged_piece != EmptySquare) {
2750 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
2751 x = dragInfo.pos.x - squareSize / 2;
2752 y = dragInfo.pos.y - squareSize / 2;
2753 DrawPieceOnDC(hdcmem, dragged_piece,
2754 ((int) dragged_piece < (int) BlackPawn),
2755 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
2758 /* Put the animated piece back into place and draw it */
2759 if (animInfo.piece != EmptySquare) {
2760 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
2761 x = boardRect.left + animInfo.pos.x;
2762 y = boardRect.top + animInfo.pos.y;
2763 DrawPieceOnDC(hdcmem, animInfo.piece,
2764 ((int) animInfo.piece < (int) BlackPawn),
2765 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
2768 /* Release the bufferBitmap by selecting in the old bitmap
2769 * and delete the memory DC
2771 SelectObject(hdcmem, oldBitmap);
2774 /* Set clipping on the target DC */
2776 SelectClipRgn(hdc, clips[0]);
2777 for (x = 1; x < num_clips; x++) {
2778 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
2779 abort(); // this should never ever happen!
2783 /* Copy the new bitmap onto the screen in one go.
2784 * This way we avoid any flickering
2786 oldBitmap = SelectObject(tmphdc, bufferBitmap);
2787 BitBlt(hdc, boardRect.left, boardRect.top,
2788 boardRect.right - boardRect.left,
2789 boardRect.bottom - boardRect.top,
2790 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
2791 SelectObject(tmphdc, oldBitmap);
2793 /* Massive cleanup */
2794 for (x = 0; x < num_clips; x++)
2795 DeleteObject(clips[x]);
2798 DeleteObject(bufferBitmap);
2801 ReleaseDC(hwndMain, hdc);
2803 if (lastDrawnFlipView != flipView) {
2805 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
2807 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
2810 CopyBoard(lastDrawn, board);
2811 lastDrawnHighlight = highlightInfo;
2812 lastDrawnPremove = premoveHighlightInfo;
2813 lastDrawnFlipView = flipView;
2818 /*---------------------------------------------------------------------------*\
2819 | CLIENT PAINT PROCEDURE
2820 | This is the main event-handler for the WM_PAINT message.
2822 \*---------------------------------------------------------------------------*/
2824 PaintProc(HWND hwnd)
2830 if(hdc = BeginPaint(hwnd, &ps)) {
2831 if (IsIconic(hwnd)) {
2832 DrawIcon(hdc, 2, 2, iconCurrent);
2834 if (!appData.monoMode) {
2835 SelectPalette(hdc, hPal, FALSE);
2836 RealizePalette(hdc);
2838 HDCDrawPosition(hdc, 1, NULL);
2840 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2841 ExtTextOut(hdc, messageRect.left, messageRect.top,
2842 ETO_CLIPPED|ETO_OPAQUE,
2843 &messageRect, messageText, strlen(messageText), NULL);
2844 SelectObject(hdc, oldFont);
2845 DisplayBothClocks();
2855 * If the user selects on a border boundary, return -1; if off the board,
2856 * return -2. Otherwise map the event coordinate to the square.
2857 * The offset boardRect.left or boardRect.top must already have been
2858 * subtracted from x.
2861 EventToSquare(int x)
2868 if ((x % (squareSize + lineGap)) >= squareSize)
2870 x /= (squareSize + lineGap);
2871 if (x >= BOARD_SIZE)
2882 DropEnable dropEnables[] = {
2883 { 'P', DP_Pawn, "Pawn" },
2884 { 'N', DP_Knight, "Knight" },
2885 { 'B', DP_Bishop, "Bishop" },
2886 { 'R', DP_Rook, "Rook" },
2887 { 'Q', DP_Queen, "Queen" },
2891 SetupDropMenu(HMENU hmenu)
2893 int i, count, enable;
2895 extern char white_holding[], black_holding[];
2898 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
2899 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2900 dropEnables[i].piece);
2902 while (p && *p++ == dropEnables[i].piece) count++;
2903 sprintf(item, "%s %d", dropEnables[i].name, count);
2904 enable = count > 0 || !appData.testLegality
2905 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2906 && !appData.icsActive);
2907 ModifyMenu(hmenu, dropEnables[i].command,
2908 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
2909 dropEnables[i].command, item);
2913 static int fromX = -1, fromY = -1, toX, toY;
2915 /* Event handler for mouse messages */
2917 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2921 static int recursive = 0;
2923 BOOLEAN saveAnimate;
2924 static BOOLEAN sameAgain = FALSE;
2927 if (message == WM_MBUTTONUP) {
2928 /* Hideous kludge to fool TrackPopupMenu into paying attention
2929 to the middle button: we simulate pressing the left button too!
2931 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
2932 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
2938 pt.x = LOWORD(lParam);
2939 pt.y = HIWORD(lParam);
2940 x = EventToSquare(pt.x - boardRect.left);
2941 y = EventToSquare(pt.y - boardRect.top);
2942 if (!flipView && y >= 0) {
2943 y = BOARD_SIZE - 1 - y;
2945 if (flipView && x >= 0) {
2946 x = BOARD_SIZE - 1 - x;
2950 case WM_LBUTTONDOWN:
2954 /* Downclick vertically off board; check if on clock */
2955 if (PtInRect((LPRECT) &whiteRect, pt)) {
2956 if (gameMode == EditPosition) {
2957 SetWhiteToPlayEvent();
2958 } else if (gameMode == IcsPlayingBlack ||
2959 gameMode == MachinePlaysWhite) {
2962 } else if (PtInRect((LPRECT) &blackRect, pt)) {
2963 if (gameMode == EditPosition) {
2964 SetBlackToPlayEvent();
2965 } else if (gameMode == IcsPlayingWhite ||
2966 gameMode == MachinePlaysBlack) {
2970 if (!appData.highlightLastMove) {
2972 DrawPosition(FALSE, NULL);
2975 dragInfo.start.x = dragInfo.start.y = -1;
2976 dragInfo.from = dragInfo.start;
2978 } else if (x < 0 || y < 0) {
2980 } else if (fromX == x && fromY == y) {
2981 /* Downclick on same square again */
2983 DrawPosition(FALSE, NULL);
2985 } else if (fromX != -1) {
2986 /* Downclick on different square */
2987 ChessSquare pdown, pup;
2988 pdown = boards[currentMove][fromY][fromX];
2989 pup = boards[currentMove][y][x];
2990 if (gameMode == EditPosition ||
2991 !((WhitePawn <= pdown && pdown <= WhiteKing &&
2992 WhitePawn <= pup && pup <= WhiteKing) ||
2993 (BlackPawn <= pdown && pdown <= BlackKing &&
2994 BlackPawn <= pup && pup <= BlackKing))) {
2995 /* EditPosition, empty square, or different color piece;
2996 click-click move is possible */
2999 if (IsPromotion(fromX, fromY, toX, toY)) {
3000 if (appData.alwaysPromoteToQueen) {
3001 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3002 if (!appData.highlightLastMove) {
3004 DrawPosition(FALSE, NULL);
3007 SetHighlights(fromX, fromY, toX, toY);
3008 DrawPosition(FALSE, NULL);
3009 PromotionPopup(hwnd);
3011 } else { /* not a promotion */
3012 if (appData.animate || appData.highlightLastMove) {
3013 SetHighlights(fromX, fromY, toX, toY);
3017 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3018 if (appData.animate && !appData.highlightLastMove) {
3020 DrawPosition(FALSE, NULL);
3023 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3028 DrawPosition(FALSE, NULL);
3030 /* First downclick, or restart on a square with same color piece */
3031 if (!frozen && OKToStartUserMove(x, y)) {
3034 dragInfo.lastpos = pt;
3035 dragInfo.from.x = fromX;
3036 dragInfo.from.y = fromY;
3037 dragInfo.start = dragInfo.from;
3038 SetCapture(hwndMain);
3041 dragInfo.start.x = dragInfo.start.y = -1;
3042 dragInfo.from = dragInfo.start;
3048 if (fromX == -1) break;
3049 if (x == fromX && y == fromY) {
3050 dragInfo.from.x = dragInfo.from.y = -1;
3051 /* Upclick on same square */
3053 /* Clicked same square twice: abort click-click move */
3056 ClearPremoveHighlights();
3058 /* First square clicked: start click-click move */
3059 SetHighlights(fromX, fromY, -1, -1);
3061 DrawPosition(FALSE, NULL);
3062 } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {
3063 /* Errant click; ignore */
3066 /* Finish drag move */
3067 dragInfo.from.x = dragInfo.from.y = -1;
3070 saveAnimate = appData.animate; /* sorry, Hawk :) */
3071 appData.animate = appData.animate && !appData.animateDragging;
3072 if (IsPromotion(fromX, fromY, toX, toY)) {
3073 if (appData.alwaysPromoteToQueen) {
3074 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3076 DrawPosition(FALSE, NULL);
3077 PromotionPopup(hwnd);
3080 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3082 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3083 appData.animate = saveAnimate;
3085 if (appData.highlightDragging && !appData.highlightLastMove) {
3088 if (appData.animate || appData.animateDragging ||
3089 appData.highlightDragging || gotPremove) {
3090 DrawPosition(FALSE, NULL);
3093 dragInfo.start.x = dragInfo.start.y = -1;
3094 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
3098 if ((appData.animateDragging || appData.highlightDragging)
3099 && (wParam & MK_LBUTTON)
3100 && dragInfo.from.x >= 0) {
3101 if (appData.animateDragging) {
3104 if (appData.highlightDragging) {
3105 SetHighlights(fromX, fromY, x, y);
3107 DrawPosition(FALSE, NULL);
3108 dragInfo.lastpos = dragInfo.pos;
3112 /* Mouse Wheel is being rolled forward
3113 * Play moves forward
3115 if ((short)HIWORD(wParam) > 0)
3116 if (forwardMostMove > 0 && currentMove != forwardMostMove)
3118 /* Mouse Wheel is being rolled backward
3119 * Play moves backward
3121 if ((short)HIWORD(wParam) < 0)
3122 if (currentMove > 0) BackwardEvent();
3124 case WM_MBUTTONDOWN:
3125 case WM_RBUTTONDOWN:
3129 dragInfo.pos.x = dragInfo.pos.y = -1;
3130 dragInfo.start.x = dragInfo.start.y = -1;
3131 dragInfo.from = dragInfo.start;
3132 dragInfo.lastpos = dragInfo.pos;
3133 if (appData.highlightDragging) {
3136 DrawPosition(TRUE, NULL);
3141 if (x < 0 || y < 0) break;
3144 if (message == WM_MBUTTONDOWN) {
3145 buttonCount = 3; /* even if system didn't think so */
3146 if (wParam & MK_SHIFT)
3147 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3149 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3150 } else { /* message == WM_RBUTTONDOWN */
3152 if (buttonCount == 3) {
3153 if (wParam & MK_SHIFT)
3154 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3156 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3158 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3161 /* Just have one menu, on the right button. Windows users don't
3162 think to try the middle one, and sometimes other software steals
3163 it, or it doesn't really exist. */
3164 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3168 case IcsPlayingWhite:
3169 case IcsPlayingBlack:
3171 case MachinePlaysWhite:
3172 case MachinePlaysBlack:
3173 if (appData.testLegality &&
3174 gameInfo.variant != VariantBughouse &&
3175 gameInfo.variant != VariantCrazyhouse) break;
3176 if (x < 0 || y < 0) break;
3179 hmenu = LoadMenu(hInst, "DropPieceMenu");
3180 SetupDropMenu(hmenu);
3181 MenuPopup(hwnd, pt, hmenu, -1);
3192 /* Preprocess messages for buttons in main window */
3194 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3196 int id = GetWindowLong(hwnd, GWL_ID);
3199 for (i=0; i<N_BUTTONS; i++) {
3200 if (buttonDesc[i].id == id) break;
3202 if (i == N_BUTTONS) return 0;
3208 dir = (wParam == VK_LEFT) ? -1 : 1;
3209 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
3216 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
3219 if (appData.icsActive) {
3220 if (GetKeyState(VK_SHIFT) < 0) {
3222 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3223 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3227 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3228 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3235 if (appData.icsActive) {
3236 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3237 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3239 SendMessage(h, WM_CHAR, wParam, lParam);
3241 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
3242 PopUpMoveDialog((char)wParam);
3248 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
3251 /* Process messages for Promotion dialog box */
3253 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
3258 case WM_INITDIALOG: /* message: initialize dialog box */
3259 /* Center the dialog over the application window */
3260 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
3261 ShowWindow(GetDlgItem(hDlg, PB_King),
3262 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3263 gameInfo.variant == VariantGiveaway) ?
3267 case WM_COMMAND: /* message: received a command */
3268 switch (LOWORD(wParam)) {
3270 EndDialog(hDlg, TRUE); /* Exit the dialog */
3272 DrawPosition(FALSE, NULL);
3292 EndDialog(hDlg, TRUE); /* Exit the dialog */
3293 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3294 if (!appData.highlightLastMove) {
3296 DrawPosition(FALSE, NULL);
3303 /* Pop up promotion dialog */
3305 PromotionPopup(HWND hwnd)
3309 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
3310 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
3311 hwnd, (DLGPROC)lpProc);
3312 FreeProcInstance(lpProc);
3315 /* Toggle ShowThinking */
3317 ToggleShowThinking()
3319 ShowThinkingEvent(!appData.showThinking);
3323 LoadGameDialog(HWND hwnd, char* title)
3327 char fileTitle[MSG_SIZ];
3328 f = OpenFileDialog(hwnd, FALSE, "",
3329 appData.oldSaveStyle ? "gam" : "pgn",
3331 title, &number, fileTitle, NULL);
3333 cmailMsgLoaded = FALSE;
3335 int error = GameListBuild(f);
3337 DisplayError("Cannot build game list", error);
3338 } else if (!ListEmpty(&gameList) &&
3339 ((ListGame *) gameList.tailPred)->number > 1) {
3340 GameListPopUp(f, fileTitle);
3346 LoadGame(f, number, fileTitle, FALSE);
3351 ChangedConsoleFont()
3354 CHARRANGE tmpsel, sel;
3355 MyFont *f = font[boardSize][CONSOLE_FONT];
3356 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
3357 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3360 cfmt.cbSize = sizeof(CHARFORMAT);
3361 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
3362 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
3363 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
3364 * size. This was undocumented in the version of MSVC++ that I had
3365 * when I wrote the code, but is apparently documented now.
3367 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
3368 cfmt.bCharSet = f->lf.lfCharSet;
3369 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
3370 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3371 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3372 /* Why are the following seemingly needed too? */
3373 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3374 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3375 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
3377 tmpsel.cpMax = -1; /*999999?*/
3378 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
3379 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
3380 /* Trying putting this here too. It still seems to tickle a RichEdit
3381 * bug: sometimes RichEdit indents the first line of a paragraph too.
3383 paraf.cbSize = sizeof(paraf);
3384 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
3385 paraf.dxStartIndent = 0;
3386 paraf.dxOffset = WRAP_INDENT;
3387 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
3388 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
3391 /*---------------------------------------------------------------------------*\
3393 * Window Proc for main window
3395 \*---------------------------------------------------------------------------*/
3397 /* Process messages for main window, etc. */
3399 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3406 char fileTitle[MSG_SIZ];
3410 case WM_PAINT: /* message: repaint portion of window */
3415 if (IsIconic(hwnd)) {
3416 /* Cheat; change the message */
3417 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
3419 return (DefWindowProc(hwnd, message, wParam, lParam));
3423 case WM_LBUTTONDOWN:
3424 case WM_MBUTTONDOWN:
3425 case WM_RBUTTONDOWN:
3431 MouseEvent(hwnd, message, wParam, lParam);
3436 if (appData.icsActive) {
3437 if (wParam == '\t') {
3438 if (GetKeyState(VK_SHIFT) < 0) {
3440 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3441 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3445 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3446 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3450 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3451 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3453 SendMessage(h, message, wParam, lParam);
3455 } else if (isalpha((char)wParam) || isdigit((char)wParam)) {
3456 PopUpMoveDialog((char)wParam);
3460 case WM_PALETTECHANGED:
3461 if (hwnd != (HWND)wParam && !appData.monoMode) {
3463 HDC hdc = GetDC(hwndMain);
3464 SelectPalette(hdc, hPal, TRUE);
3465 nnew = RealizePalette(hdc);
3467 paletteChanged = TRUE;
3471 InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/
3474 ReleaseDC(hwnd, hdc);
3478 case WM_QUERYNEWPALETTE:
3479 if (!appData.monoMode /*&& paletteChanged*/) {
3481 HDC hdc = GetDC(hwndMain);
3482 paletteChanged = FALSE;
3483 SelectPalette(hdc, hPal, FALSE);
3484 nnew = RealizePalette(hdc);
3486 InvalidateRect(hwnd, &boardRect, FALSE);
3488 ReleaseDC(hwnd, hdc);
3493 case WM_COMMAND: /* message: command from application menu */
3494 wmId = LOWORD(wParam);
3495 wmEvent = HIWORD(wParam);
3504 LoadGameDialog(hwnd, "Load Game from File");
3507 case IDM_LoadNextGame:
3511 case IDM_LoadPrevGame:
3515 case IDM_ReloadGame:
3519 case IDM_LoadPosition:
3520 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
3524 f = OpenFileDialog(hwnd, FALSE, "",
3525 appData.oldSaveStyle ? "pos" : "fen",
3527 "Load Position from File", &number, fileTitle, NULL);
3529 LoadPosition(f, number, fileTitle);
3533 case IDM_LoadNextPosition:
3537 case IDM_LoadPrevPosition:
3541 case IDM_ReloadPosition:
3546 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
3547 f = OpenFileDialog(hwnd, TRUE, defName,
3548 appData.oldSaveStyle ? "gam" : "pgn",
3550 "Save Game to File", NULL, fileTitle, NULL);
3556 case IDM_SavePosition:
3557 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
3558 f = OpenFileDialog(hwnd, TRUE, defName,
3559 appData.oldSaveStyle ? "pos" : "fen",
3561 "Save Position to File", NULL, fileTitle, NULL);
3563 SavePosition(f, 0, "");
3568 CopyGameToClipboard();
3572 PasteGameFromClipboard();
3575 case IDM_CopyPosition:
3576 CopyFENToClipboard();
3579 case IDM_PastePosition:
3580 PasteFENFromClipboard();
3587 case IDM_ReloadCMailMsg:
3589 ReloadCmailMsgEvent(FALSE);
3593 ShowWindow(hwnd, SW_MINIMIZE);
3600 case IDM_MachineWhite:
3601 MachineWhiteEvent();
3603 * refresh the tags dialog only if it's visible
3605 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
3607 tags = PGNTags(&gameInfo);
3608 TagsPopUp(tags, CmailMsg());
3613 case IDM_MachineBlack:
3614 MachineBlackEvent();
3616 * refresh the tags dialog only if it's visible
3618 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
3620 tags = PGNTags(&gameInfo);
3621 TagsPopUp(tags, CmailMsg());
3626 case IDM_TwoMachines:
3629 * refresh the tags dialog only if it's visible
3631 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
3633 tags = PGNTags(&gameInfo);
3634 TagsPopUp(tags, CmailMsg());
3639 case IDM_AnalysisMode:
3640 if (!first.analysisSupport) {
3642 sprintf(buf, "%s does not support analysis", first.tidy);
3643 DisplayError(buf, 0);
3645 if (!appData.showThinking) ToggleShowThinking();
3650 case IDM_AnalyzeFile:
3651 if (!first.analysisSupport) {
3653 sprintf(buf, "%s does not support analysis", first.tidy);
3654 DisplayError(buf, 0);
3656 if (!appData.showThinking) ToggleShowThinking();
3658 LoadGameDialog(hwnd, "Analyze Game from File");
3659 AnalysisPeriodicEvent(1);
3671 case IDM_EditPosition:
3672 EditPositionEvent();
3679 case IDM_ShowGameList:
3687 case IDM_EditComment:
3688 if (commentDialogUp && editComment) {
3731 case IDM_StopObserving:
3732 StopObservingEvent();
3735 case IDM_StopExamining:
3736 StopExaminingEvent();
3739 case IDM_TypeInMove:
3740 PopUpMoveDialog('\000');
3767 case IDM_TruncateGame:
3768 TruncateGameEvent();
3775 case IDM_RetractMove:
3780 flipView = !flipView;
3781 DrawPosition(FALSE, NULL);
3784 case IDM_GeneralOptions:
3785 GeneralOptionsPopup(hwnd);
3788 case IDM_BoardOptions:
3789 BoardOptionsPopup(hwnd);
3792 case IDM_IcsOptions:
3793 IcsOptionsPopup(hwnd);
3797 FontsOptionsPopup(hwnd);
3801 SoundOptionsPopup(hwnd);
3805 CommPortOptionsPopup(hwnd);
3808 case IDM_LoadOptions:
3809 LoadOptionsPopup(hwnd);
3812 case IDM_SaveOptions:
3813 SaveOptionsPopup(hwnd);
3816 case IDM_TimeControl:
3817 TimeControlOptionsPopup(hwnd);
3820 case IDM_SaveSettings:
3821 SaveSettings(settingsFileName);
3824 case IDM_SaveSettingsOnExit:
3825 saveSettingsOnExit = !saveSettingsOnExit;
3826 (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
3827 MF_BYCOMMAND|(saveSettingsOnExit ?
3828 MF_CHECKED : MF_UNCHECKED));
3844 appData.debugMode = !appData.debugMode;
3845 if (appData.debugMode) {
3847 GetCurrentDirectory(MSG_SIZ, dir);
3848 SetCurrentDirectory(installDir);
3849 debugFP = fopen("WinBoard.debug", "w");
3850 SetCurrentDirectory(dir);
3851 setbuf(debugFP, NULL);
3858 case IDM_HELPCONTENTS:
3859 if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
3860 MessageBox (GetFocus(),
3861 "Unable to activate help",
3862 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3866 case IDM_HELPSEARCH:
3867 if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
3868 MessageBox (GetFocus(),
3869 "Unable to activate help",
3870 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3875 if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
3876 MessageBox (GetFocus(),
3877 "Unable to activate help",
3878 szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3883 lpProc = MakeProcInstance((FARPROC)About, hInst);
3885 (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
3886 "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
3887 FreeProcInstance(lpProc);
3890 case IDM_DirectCommand1:
3891 AskQuestionEvent("Direct Command",
3892 "Send to chess program:", "", "1");
3894 case IDM_DirectCommand2:
3895 AskQuestionEvent("Direct Command",
3896 "Send to second chess program:", "", "2");
3900 EditPositionMenuEvent(WhitePawn, fromX, fromY);
3904 case EP_WhiteKnight:
3905 EditPositionMenuEvent(WhiteKnight, fromX, fromY);
3909 case EP_WhiteBishop:
3910 EditPositionMenuEvent(WhiteBishop, fromX, fromY);
3915 EditPositionMenuEvent(WhiteRook, fromX, fromY);
3920 EditPositionMenuEvent(WhiteQueen, fromX, fromY);
3925 EditPositionMenuEvent(WhiteKing, fromX, fromY);
3930 EditPositionMenuEvent(BlackPawn, fromX, fromY);
3934 case EP_BlackKnight:
3935 EditPositionMenuEvent(BlackKnight, fromX, fromY);
3939 case EP_BlackBishop:
3940 EditPositionMenuEvent(BlackBishop, fromX, fromY);
3945 EditPositionMenuEvent(BlackRook, fromX, fromY);
3950 EditPositionMenuEvent(BlackQueen, fromX, fromY);
3955 EditPositionMenuEvent(BlackKing, fromX, fromY);
3959 case EP_EmptySquare:
3960 EditPositionMenuEvent(EmptySquare, fromX, fromY);
3965 EditPositionMenuEvent(ClearBoard, fromX, fromY);
3970 EditPositionMenuEvent(WhitePlay, fromX, fromY);
3975 EditPositionMenuEvent(BlackPlay, fromX, fromY);
3980 DropMenuEvent(WhitePawn, fromX, fromY);
3985 DropMenuEvent(WhiteKnight, fromX, fromY);
3990 DropMenuEvent(WhiteBishop, fromX, fromY);
3995 DropMenuEvent(WhiteRook, fromX, fromY);
4000 DropMenuEvent(WhiteQueen, fromX, fromY);
4005 return (DefWindowProc(hwnd, message, wParam, lParam));
4011 case CLOCK_TIMER_ID:
4012 KillTimer(hwnd, clockTimerEvent); /* Simulate one-shot timer as in X */
4013 clockTimerEvent = 0;
4014 DecrementClocks(); /* call into back end */
4016 case LOAD_GAME_TIMER_ID:
4017 KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
4018 loadGameTimerEvent = 0;
4019 AutoPlayGameLoop(); /* call into back end */
4021 case ANALYSIS_TIMER_ID:
4022 if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
4023 appData.periodicUpdates) {
4024 AnalysisPeriodicEvent(0);
4026 KillTimer(hwnd, analysisTimerEvent);
4027 analysisTimerEvent = 0;
4030 case DELAYED_TIMER_ID:
4031 KillTimer(hwnd, delayedTimerEvent);
4032 delayedTimerEvent = 0;
4033 delayedTimerCallback();
4039 InputEvent(hwnd, message, wParam, lParam);
4042 case WM_ENTERSIZEMOVE:
4043 if (hwnd == hwndMain) {
4050 if (hwnd == hwndMain) {
4051 lastSizing = wParam;
4055 case WM_EXITSIZEMOVE:
4056 if (hwnd == hwndMain) {
4058 doingSizing = FALSE;
4059 InvalidateRect(hwnd, &boardRect, FALSE);
4060 GetClientRect(hwnd, &client);
4061 ResizeBoard(client.right, client.bottom, lastSizing);
4066 case WM_DESTROY: /* message: window being destroyed */
4071 if (hwnd == hwndMain) {
4076 default: /* Passes it on if unprocessed */
4077 return (DefWindowProc(hwnd, message, wParam, lParam));
4082 /*---------------------------------------------------------------------------*\
4084 * Misc utility routines
4086 \*---------------------------------------------------------------------------*/
4089 * Decent random number generator, at least not as bad as Windows
4090 * standard rand, which returns a value in the range 0 to 0x7fff.
4092 unsigned int randstate;
4097 randstate = randstate * 1664525 + 1013904223;
4098 return (int) randstate & 0x7fffffff;
4102 mysrandom(unsigned int seed)
4109 * returns TRUE if user selects a different color, FALSE otherwise
4113 ChangeColor(HWND hwnd, COLORREF *which)
4115 static BOOL firstTime = TRUE;
4116 static DWORD customColors[16];
4123 /* Make initial colors in use available as custom colors */
4124 /* Should we put the compiled-in defaults here instead? */
4126 customColors[i++] = lightSquareColor & 0xffffff;
4127 customColors[i++] = darkSquareColor & 0xffffff;
4128 customColors[i++] = whitePieceColor & 0xffffff;
4129 customColors[i++] = blackPieceColor & 0xffffff;
4130 customColors[i++] = highlightSquareColor & 0xffffff;
4131 customColors[i++] = premoveHighlightColor & 0xffffff;
4133 for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
4134 customColors[i++] = textAttribs[ccl].color;
4136 while (i < 16) customColors[i++] = RGB(255, 255, 255);
4140 cc.lStructSize = sizeof(cc);
4141 cc.hwndOwner = hwnd;
4142 cc.hInstance = NULL;
4143 cc.rgbResult = (DWORD) (*which & 0xffffff);
4144 cc.lpCustColors = (LPDWORD) customColors;
4145 cc.Flags = CC_RGBINIT|CC_FULLOPEN;
4147 if (!ChooseColor(&cc)) return FALSE;
4149 newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
4150 if (newcolor == *which) return FALSE;
4155 InitDrawingColors();
4156 InvalidateRect(hwnd, &boardRect, FALSE);
4161 MyLoadSound(MySound *ms)
4167 if (ms->data) free(ms->data);
4170 switch (ms->name[0]) {
4176 /* System sound from Control Panel. Don't preload here. */
4180 if (ms->name[1] == NULLCHAR) {
4181 /* "!" alone = silence */
4184 /* Builtin wave resource. Error if not found. */
4185 HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
4186 if (h == NULL) break;
4187 ms->data = (void *)LoadResource(hInst, h);
4188 if (h == NULL) break;
4193 /* .wav file. Error if not found. */
4194 f = fopen(ms->name, "rb");
4195 if (f == NULL) break;
4196 if (fstat(fileno(f), &st) < 0) break;
4197 ms->data = malloc(st.st_size);
4198 if (fread(ms->data, st.st_size, 1, f) < 1) break;
4205 sprintf(buf, "Error loading sound %s", ms->name);
4206 DisplayError(buf, GetLastError());
4212 MyPlaySound(MySound *ms)
4215 switch (ms->name[0]) {
4221 /* System sound from Control Panel (deprecated feature).
4222 "$" alone or an unset sound name gets default beep (still in use). */
4224 ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
4226 if (!ok) ok = MessageBeep(MB_OK);
4229 /* Builtin wave resource, or "!" alone for silence */
4231 if (ms->data == NULL) return FALSE;
4232 ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);