2 * WinBoard.c -- Windows NT front end to XBoard
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6 * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
8 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
9 * which was written and is copyrighted by Wayne Christopher.
11 * The following terms apply to Digital Equipment Corporation's copyright
13 * ------------------------------------------------------------------------
16 * Permission to use, copy, modify, and distribute this software and its
17 * documentation for any purpose and without fee is hereby granted,
18 * provided that the above copyright notice appear in all copies and that
19 * both that copyright notice and this permission notice appear in
20 * supporting documentation, and that the name of Digital not be
21 * used in advertising or publicity pertaining to distribution of the
22 * software without specific, written prior permission.
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
31 * ------------------------------------------------------------------------
33 * The following terms apply to the enhanced version of XBoard distributed
34 * by the Free Software Foundation:
35 * ------------------------------------------------------------------------
36 * This program is free software; you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation; either version 2 of the License, or
39 * (at your option) any later version.
41 * This program is distributed in the hope that it will be useful,
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
44 * GNU General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program; if not, write to the Free Software
48 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
49 * ------------------------------------------------------------------------
80 #include "wgamelist.h"
81 #include "wedittags.h"
88 POINT pos; /* window coordinates of current pos */
89 POINT lastpos; /* window coordinates of last pos - used for clipping */
90 POINT from; /* board coordinates of the piece's orig pos */
91 POINT to; /* board coordinates of the piece's new pos */
94 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
97 POINT start; /* window coordinates of start pos */
98 POINT pos; /* window coordinates of current pos */
99 POINT lastpos; /* window coordinates of last pos - used for clipping */
100 POINT from; /* board coordinates of the piece's orig pos */
103 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
106 POINT sq[2]; /* board coordinates of from, to squares */
109 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
110 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
112 /* Window class names */
113 char szAppName[] = "WinBoard";
114 char szConsoleName[] = "WBConsole";
117 char szTitle[] = "WinBoard";
118 char szConsoleTitle[] = "ICS Interaction";
121 char *settingsFileName;
122 BOOLEAN saveSettingsOnExit;
123 char installDir[MSG_SIZ];
126 BOOLEAN chessProgram;
127 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;
128 static int squareSize, lineGap;
129 static int winWidth, winHeight;
130 static RECT messageRect, whiteRect, blackRect;
131 static char messageText[MESSAGE_TEXT_MAX];
132 static int clockTimerEvent = 0;
133 static int loadGameTimerEvent = 0;
134 static int analysisTimerEvent = 0;
135 static DelayedEventCallback delayedTimerCallback;
136 static int delayedTimerEvent = 0;
137 static int buttonCount = 2;
138 char *icsTextMenuString;
140 char *firstChessProgramNames;
141 char *secondChessProgramNames;
143 #define ARG_MAX 20000
145 #define PALETTESIZE 256
147 HINSTANCE hInst; /* current instance */
148 HWND hwndMain = NULL; /* root window*/
149 HWND hwndConsole = NULL;
150 BOOLEAN alwaysOnTop = FALSE;
152 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
153 blackPieceColor, highlightSquareColor, premoveHighlightColor;
155 ColorClass currentColorClass;
157 HWND hCommPort = NULL; /* currently open comm port */
158 static HWND hwndPause; /* pause button */
159 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];
160 static HBRUSH lightSquareBrush, darkSquareBrush,
161 whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;
162 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];
163 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];
164 static HPEN gridPen = NULL;
165 static HPEN highlightPen = NULL;
166 static HPEN premovePen = NULL;
167 static NPLOGPALETTE pLogPal;
168 static BOOL paletteChanged = FALSE;
169 static HICON iconWhite, iconBlack, iconCurrent;
170 static int doingSizing = FALSE;
171 static int lastSizing = 0;
172 static int prevStderrPort;
174 #if __GNUC__ && !defined(_winmajor)
175 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
177 #define oldDialog (_winmajor < 4)
180 char *defaultTextAttribs[] =
182 COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,
183 COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,
193 int cliWidth, cliHeight;
196 SizeInfo sizeInfo[] =
198 { "tiny", 21, 0, 1, 1, 0, 0 },
199 { "teeny", 25, 1, 1, 1, 0, 0 },
200 { "dinky", 29, 1, 1, 1, 0, 0 },
201 { "petite", 33, 1, 1, 1, 0, 0 },
202 { "slim", 37, 2, 1, 0, 0, 0 },
203 { "small", 40, 2, 1, 0, 0, 0 },
204 { "mediocre", 45, 2, 1, 0, 0, 0 },
205 { "middling", 49, 2, 0, 0, 0, 0 },
206 { "average", 54, 2, 0, 0, 0, 0 },
207 { "moderate", 58, 3, 0, 0, 0, 0 },
208 { "medium", 64, 3, 0, 0, 0, 0 },
209 { "bulky", 72, 3, 0, 0, 0, 0 },
210 { "large", 80, 3, 0, 0, 0, 0 },
211 { "big", 87, 3, 0, 0, 0, 0 },
212 { "huge", 95, 3, 0, 0, 0, 0 },
213 { "giant", 108, 3, 0, 0, 0, 0 },
214 { "colossal", 116, 4, 0, 0, 0, 0 },
215 { "titanic", 129, 4, 0, 0, 0, 0 },
216 { NULL, 0, 0, 0, 0, 0, 0 }
219 #define MF(x) {x, {0, }, {0, }, 0}
220 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
222 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY),
223 MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),
224 MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },
225 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY),
226 MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),
227 MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },
228 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),
229 MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),
230 MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },
231 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),
232 MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),
233 MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },
234 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),
235 MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),
236 MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },
237 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),
238 MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),
239 MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },
240 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),
241 MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),
242 MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },
243 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),
244 MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),
245 MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },
246 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),
247 MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),
248 MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },
249 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),
250 MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),
251 MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },
252 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),
253 MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),
254 MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },
255 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),
256 MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),
257 MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },
258 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),
259 MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),
260 MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },
261 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),
262 MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),
263 MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },
264 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),
265 MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),
266 MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },
267 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),
268 MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),
269 MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },
270 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),
271 MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),
272 MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },
273 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),
274 MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),
275 MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },
278 MyFont *font[NUM_SIZES][NUM_FONTS];
287 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
290 MyButtonDesc buttonDesc[N_BUTTONS] =
292 {"<<", IDM_ToStart, NULL, NULL},
293 {"<", IDM_Backward, NULL, NULL},
294 {"P", IDM_Pause, NULL, NULL},
295 {">", IDM_Forward, NULL, NULL},
296 {">>", IDM_ToEnd, NULL, NULL},
299 int tinyLayout = 0, smallLayout = 0;
300 #define MENU_BAR_ITEMS 6
301 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
302 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
303 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
307 MySound sounds[(int)NSoundClasses];
308 MyTextAttribs textAttribs[(int)NColorClasses];
310 MyColorizeAttribs colorizeAttribs[] = {
311 { (COLORREF)0, 0, "Shout Text" },
312 { (COLORREF)0, 0, "SShout/CShout" },
313 { (COLORREF)0, 0, "Channel 1 Text" },
314 { (COLORREF)0, 0, "Channel Text" },
315 { (COLORREF)0, 0, "Kibitz Text" },
316 { (COLORREF)0, 0, "Tell Text" },
317 { (COLORREF)0, 0, "Challenge Text" },
318 { (COLORREF)0, 0, "Request Text" },
319 { (COLORREF)0, 0, "Seek Text" },
320 { (COLORREF)0, 0, "Normal Text" },
321 { (COLORREF)0, 0, "None" }
326 static char *commentTitle;
327 static char *commentText;
328 static int commentIndex;
329 static Boolean editComment = FALSE;
330 HWND commentDialog = NULL;
331 BOOLEAN commentDialogUp = FALSE;
332 static int commentX, commentY, commentH, commentW;
334 static char *analysisTitle;
335 static char *analysisText;
336 HWND analysisDialog = NULL;
337 BOOLEAN analysisDialogUp = FALSE;
338 static int analysisX, analysisY, analysisH, analysisW;
340 char errorTitle[MSG_SIZ];
341 char errorMessage[2*MSG_SIZ];
342 HWND errorDialog = NULL;
343 BOOLEAN moveErrorMessageUp = FALSE;
344 BOOLEAN consoleEcho = TRUE;
345 CHARFORMAT consoleCF;
346 COLORREF consoleBackgroundColor;
348 char *programVersion;
363 SOCKET sock2; /* stderr socket for OpenRcmd */
366 #define INPUT_SOURCE_BUF_SIZE 4096
368 typedef struct _InputSource {
375 char buf[INPUT_SOURCE_BUF_SIZE];
380 struct _InputSource *second; /* for stderr thread on CPRcmd */
384 InputSource *consoleInputSource;
389 VOID ConsoleOutput(char* data, int length, int forceVisible);
390 VOID ConsoleCreate();
392 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
393 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
394 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
395 VOID ParseCommSettings(char *arg, DCB *dcb);
397 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
398 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
399 void ParseIcsTextMenu(char *icsTextMenuString);
400 VOID PopUpMoveDialog(char firstchar);
401 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
404 * Setting "frozen" should disable all user input other than deleting
405 * the window. We do this while engines are initializing themselves.
407 static int frozen = 0;
408 static int oldMenuItemState[MENU_BAR_ITEMS];
416 hmenu = GetMenu(hwndMain);
417 for (i=0; i<MENU_BAR_ITEMS; i++) {
418 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
420 DrawMenuBar(hwndMain);
423 /* Undo a FreezeUI */
431 hmenu = GetMenu(hwndMain);
432 for (i=0; i<MENU_BAR_ITEMS; i++) {
433 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
435 DrawMenuBar(hwndMain);
438 /*---------------------------------------------------------------------------*\
442 \*---------------------------------------------------------------------------*/
445 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
446 LPSTR lpCmdLine, int nCmdShow)
449 HANDLE hAccelMain, hAccelNoAlt;
453 LoadLibrary("RICHED32.DLL");
454 consoleCF.cbSize = sizeof(CHARFORMAT);
456 if (!InitApplication(hInstance)) {
459 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
463 hAccelMain = LoadAccelerators (hInstance, szAppName);
464 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
466 /* Acquire and dispatch messages until a WM_QUIT message is received. */
468 while (GetMessage(&msg, /* message structure */
469 NULL, /* handle of window receiving the message */
470 0, /* lowest message to examine */
471 0)) /* highest message to examine */
473 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
474 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
475 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
476 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
477 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&
478 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
479 TranslateMessage(&msg); /* Translates virtual key codes */
480 DispatchMessage(&msg); /* Dispatches message to window */
485 return (msg.wParam); /* Returns the value from PostQuitMessage */
488 /*---------------------------------------------------------------------------*\
490 * Initialization functions
492 \*---------------------------------------------------------------------------*/
495 InitApplication(HINSTANCE hInstance)
499 /* Fill in window class structure with parameters that describe the */
502 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
503 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
504 wc.cbClsExtra = 0; /* No per-class extra data. */
505 wc.cbWndExtra = 0; /* No per-window extra data. */
506 wc.hInstance = hInstance; /* Owner of this class */
507 wc.hIcon = LoadIcon(hInstance, "icon_white");
508 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
509 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
510 wc.lpszMenuName = szAppName; /* Menu name from .RC */
511 wc.lpszClassName = szAppName; /* Name to register as */
513 /* Register the window class and return success/failure code. */
514 if (!RegisterClass(&wc)) return FALSE;
516 wc.style = CS_HREDRAW | CS_VREDRAW;
517 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
519 wc.cbWndExtra = DLGWINDOWEXTRA;
520 wc.hInstance = hInstance;
521 wc.hIcon = LoadIcon(hInstance, "icon_white");
522 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
523 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
524 wc.lpszMenuName = NULL;
525 wc.lpszClassName = szConsoleName;
527 if (!RegisterClass(&wc)) return FALSE;
532 /* Set by InitInstance, used by EnsureOnScreen */
533 int screenHeight, screenWidth;
536 EnsureOnScreen(int *x, int *y)
538 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
539 if (*x > screenWidth - 32) *x = 0;
540 if (*y > screenHeight - 32) *y = 0;
544 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
546 HWND hwnd; /* Main window handle. */
551 hInst = hInstance; /* Store instance handle in our global variable */
553 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
554 *filepart = NULLCHAR;
556 GetCurrentDirectory(MSG_SIZ, installDir);
558 InitAppData(lpCmdLine); /* Get run-time parameters */
559 if (appData.debugMode) {
560 debugFP = fopen("winboard.debug", "w");
561 setbuf(debugFP, NULL);
566 /* Create a main window for this application instance. */
567 hwnd = CreateWindow(szAppName, szTitle,
568 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
569 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
570 NULL, NULL, hInstance, NULL);
573 /* If window could not be created, return "failure" */
578 iconWhite = LoadIcon(hInstance, "icon_white");
579 iconBlack = LoadIcon(hInstance, "icon_black");
580 iconCurrent = iconWhite;
582 screenHeight = GetSystemMetrics(SM_CYSCREEN);
583 screenWidth = GetSystemMetrics(SM_CXSCREEN);
584 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
585 /* Compute window size for each board size, and use the largest
586 size that fits on this screen as the default. */
587 InitDrawingSizes((BoardSize)ibs, 0);
588 if (boardSize == (BoardSize)-1 &&
589 winHeight <= screenHeight && winWidth <= screenWidth) {
590 boardSize = (BoardSize)ibs;
593 InitDrawingSizes(boardSize, 0);
595 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
599 /* Make the window visible; update its client area; and return "success" */
600 EnsureOnScreen(&boardX, &boardY);
601 wp.length = sizeof(WINDOWPLACEMENT);
603 wp.showCmd = nCmdShow;
604 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
605 wp.rcNormalPosition.left = boardX;
606 wp.rcNormalPosition.right = boardX + winWidth;
607 wp.rcNormalPosition.top = boardY;
608 wp.rcNormalPosition.bottom = boardY + winHeight;
609 SetWindowPlacement(hwndMain, &wp);
611 SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
612 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
615 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
616 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
618 ShowWindow(hwndConsole, nCmdShow);
628 ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone,
629 ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,
638 String *pString; // ArgString
640 float *pFloat; // ArgFloat
641 Boolean *pBoolean; // ArgBoolean
642 COLORREF *pColor; // ArgColor
643 ColorClass cc; // ArgAttribs
644 String *pFilename; // ArgFilename
645 BoardSize *pBoardSize; // ArgBoardSize
646 int whichFont; // ArgFont
647 DCB *pDCB; // ArgCommSettings
648 String *pFilename; // ArgSettingsFilename
656 ArgDescriptor argDescriptors[] = {
657 /* positional arguments */
658 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
659 { "", ArgNone, NULL },
660 /* keyword arguments */
661 { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },
662 { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },
663 { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },
664 { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },
665 { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },
666 { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },
667 { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },
668 { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },
669 { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },
670 { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },
671 { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },
672 { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },
673 { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },
674 { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },
675 { "initString", ArgString, (LPVOID) &appData.initString, FALSE },
676 { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },
677 { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },
678 { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,
680 { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,
682 { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,
684 { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },
685 { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,
687 { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },
688 { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },
689 { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },
690 { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
691 { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
692 { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },
693 { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },
694 { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
695 { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
696 { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },
697 { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },
698 { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },
699 { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },
700 { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
701 { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
702 { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
703 { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
704 /*!!bitmapDirectory?*/
705 { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
706 { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
707 { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
708 { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
709 { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },
710 { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },
711 { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },
712 { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },
713 { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },
714 { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },
715 { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },
716 { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },
717 { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
718 { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
719 { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },
720 { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },
721 { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },
722 { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },
723 { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
724 { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
725 { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
726 { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
727 { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
728 { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
729 { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },
730 { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },
731 { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
732 { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
733 { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },
734 { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },
735 { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },
736 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
737 { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
738 { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
739 { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
740 { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },
741 { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },
742 { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },
743 { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },
744 { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
745 { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
746 { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
747 { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
748 { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
749 { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
750 { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
751 { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
752 { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },
753 { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },
754 { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
755 { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
756 { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },
757 { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },
758 { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },
759 { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },
760 { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
761 { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
762 { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },
763 { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },
764 { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
765 { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
766 { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },
767 { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },
768 { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
769 { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
770 { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },
771 { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },
772 { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
773 { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
774 { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },
775 { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },
776 { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
777 { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
778 { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },
779 { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },
780 { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
781 { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
782 { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },
783 { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },
784 { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
785 { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
786 { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },
787 { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },
788 { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
789 { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
790 { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },
791 { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },
792 { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
793 { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
794 { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },
795 { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },
796 { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
797 { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
798 { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors,
799 FALSE }, /* only so that old WinBoard.ini files from betas can be read */
800 { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },
801 { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },
802 { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },
803 { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },
804 { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },
805 { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },
806 { "boardSize", ArgBoardSize, (LPVOID) &boardSize,
807 TRUE }, /* must come after all fonts */
808 { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },
809 { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,
810 FALSE }, /* historical; kept only so old winboard.ini files will parse */
811 { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },
812 { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },
813 { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
814 { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
815 { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },
816 { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },
817 { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
818 { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
819 { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },
820 { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },
821 { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
822 { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
823 { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },
824 { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },
825 { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
826 { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
827 { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },
828 { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },
829 { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
830 { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
831 { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },
832 { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },
833 { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
834 { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
835 { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },
836 { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },
837 { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
838 { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
840 { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
841 { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
843 { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },
844 { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
845 { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
846 { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
847 { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },
848 { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },
849 { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
850 { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
851 { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },
852 { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },
853 { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
854 { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
855 { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },
856 { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },
857 { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
858 { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
859 { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },
860 { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },
861 { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
862 { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
863 { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },
864 { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },
865 { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },
866 { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },
867 { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },
868 { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },
869 { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
870 { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
871 { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },
872 { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },
873 { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },
874 { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
875 { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
876 { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },
877 { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},
878 { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},
879 { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
880 { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
881 { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},
882 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
883 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
884 { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },
885 { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
886 { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
887 { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },
888 { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },
889 { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },
890 { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },
891 { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },
892 { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },
893 { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },
894 { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
895 { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
896 { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },
897 { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },
898 { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
899 { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
900 { "highlightLastMove", ArgBoolean,
901 (LPVOID) &appData.highlightLastMove, TRUE },
902 { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },
903 { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
904 { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
905 { "highlightDragging", ArgBoolean,
906 (LPVOID) &appData.highlightDragging, TRUE },
907 { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },
908 { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
909 { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
910 { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },
911 { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },
912 { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
913 { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
914 { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },
915 { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },
916 { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },
917 { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },
918 { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },
919 { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },
920 { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },
921 { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },
922 { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },
923 { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },
924 { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },
925 { "soundShout", ArgFilename,
926 (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },
927 { "soundSShout", ArgFilename,
928 (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },
929 { "soundChannel1", ArgFilename,
930 (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },
931 { "soundChannel", ArgFilename,
932 (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },
933 { "soundKibitz", ArgFilename,
934 (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },
935 { "soundTell", ArgFilename,
936 (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },
937 { "soundChallenge", ArgFilename,
938 (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },
939 { "soundRequest", ArgFilename,
940 (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },
941 { "soundSeek", ArgFilename,
942 (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },
943 { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },
944 { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },
945 { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },
946 { "soundIcsLoss", ArgFilename,
947 (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },
948 { "soundIcsDraw", ArgFilename,
949 (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },
950 { "soundIcsUnfinished", ArgFilename,
951 (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},
952 { "soundIcsAlarm", ArgFilename,
953 (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },
954 { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },
955 { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },
956 { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
957 { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
958 { "reuseChessPrograms", ArgBoolean,
959 (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */
960 { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },
961 { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },
962 { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
963 { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
964 { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },
965 { "x", ArgInt, (LPVOID) &boardX, TRUE },
966 { "y", ArgInt, (LPVOID) &boardY, TRUE },
967 { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },
968 { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },
969 { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },
970 { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },
971 { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },
972 { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },
973 { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },
974 { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },
975 { "commentX", ArgInt, (LPVOID) &commentX, TRUE },
976 { "commentY", ArgInt, (LPVOID) &commentY, TRUE },
977 { "commentW", ArgInt, (LPVOID) &commentW, TRUE },
978 { "commentH", ArgInt, (LPVOID) &commentH, TRUE },
979 { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },
980 { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },
981 { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },
982 { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },
983 { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },
984 { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },
985 { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },
986 { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },
987 { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
988 { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
989 { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },
990 { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },
991 { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },
992 { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },
993 { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },
994 { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },
995 { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },
996 { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,
998 { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,
1000 { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1001 { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1002 { "variant", ArgString, (LPVOID) &appData.variant, FALSE },
1003 { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,
1005 { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,
1007 { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },
1008 { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },
1009 { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1010 { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1012 { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },
1013 { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },
1014 { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1015 { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1016 { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },
1017 { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },
1018 { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1019 { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1020 { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },
1021 { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },
1022 { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },
1023 { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },
1024 { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,
1026 { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },
1027 { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },
1028 { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },
1029 { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1030 { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1031 { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },
1032 { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,
1034 { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1035 { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1036 { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1037 { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },
1038 { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },
1039 { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },
1040 { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },
1041 { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1042 { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1043 { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },
1044 { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },
1045 { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1046 { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1047 { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },
1048 { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },
1049 { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },
1050 /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */
1051 { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },
1053 { NULL, ArgNone, NULL, FALSE }
1057 /* Kludge for indirection files on command line */
1058 char* lastIndirectionFilename;
1059 ArgDescriptor argDescriptorIndirection =
1060 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };
1064 ExitArgError(char *msg, char *badArg)
1068 sprintf(buf, "%s %s", msg, badArg);
1069 DisplayFatalError(buf, 0, 2);
1073 /* Command line font name parser. NULL name means do nothing.
1074 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
1075 For backward compatibility, syntax without the colon is also
1076 accepted, but font names with digits in them won't work in that case.
1079 ParseFontName(char *name, MyFontParams *mfp)
1082 if (name == NULL) return;
1086 if (q - p >= sizeof(mfp->faceName))
1087 ExitArgError("Font name too long:", name);
1088 memcpy(mfp->faceName, p, q - p);
1089 mfp->faceName[q - p] = NULLCHAR;
1093 while (*p && !isdigit(*p)) {
1095 if (q - mfp->faceName >= sizeof(mfp->faceName))
1096 ExitArgError("Font name too long:", name);
1098 while (q > mfp->faceName && q[-1] == ' ') q--;
1101 if (!*p) ExitArgError("Font point size missing:", name);
1102 mfp->pointSize = (float) atof(p);
1103 mfp->bold = (strchr(p, 'b') != NULL);
1104 mfp->italic = (strchr(p, 'i') != NULL);
1105 mfp->underline = (strchr(p, 'u') != NULL);
1106 mfp->strikeout = (strchr(p, 's') != NULL);
1109 /* Color name parser.
1110 X version accepts X color names, but this one
1111 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
1113 ParseColorName(char *name)
1115 int red, green, blue, count;
1118 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
1120 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
1121 &red, &green, &blue);
1124 sprintf(buf, "Can't parse color name %s", name);
1125 DisplayError(buf, 0);
1126 return RGB(0, 0, 0);
1128 return PALETTERGB(red, green, blue);
1132 void ParseAttribs(COLORREF *color, int *effects, char* argValue)
1138 if (*e == 'b') eff |= CFE_BOLD;
1139 else if (*e == 'i') eff |= CFE_ITALIC;
1140 else if (*e == 'u') eff |= CFE_UNDERLINE;
1141 else if (*e == 's') eff |= CFE_STRIKEOUT;
1142 else if (*e == '#' || isdigit(*e)) break;
1146 *color = ParseColorName(e);
1151 ParseBoardSize(char *name)
1153 BoardSize bs = SizeTiny;
1154 while (sizeInfo[bs].name != NULL) {
1155 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;
1158 ExitArgError("Unrecognized board size value", name);
1159 return bs; /* not reached */
1164 StringGet(void *getClosure)
1166 char **p = (char **) getClosure;
1171 FileGet(void *getClosure)
1174 FILE* f = (FILE*) getClosure;
1183 /* Parse settings file named "name". If file found, return the
1184 full name in fullname and return TRUE; else return FALSE */
1186 ParseSettingsFile(char *name, char fullname[MSG_SIZ])
1191 if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {
1192 f = fopen(fullname, "r");
1194 ParseArgs(FileGet, f);
1203 ParseArgs(GetFunc get, void *cl)
1205 char argName[ARG_MAX];
1206 char argValue[ARG_MAX];
1216 while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);
1217 if (ch == NULLCHAR) break;
1219 /* Comment to end of line */
1221 while (ch != '\n' && ch != NULLCHAR) ch = get(cl);
1223 } else if (ch == '/' || ch == '-') {
1226 while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&
1227 ch != '\n' && ch != '\t') {
1233 for (ad = argDescriptors; ad->argName != NULL; ad++)
1234 if (strcmp(ad->argName, argName + 1) == 0) break;
1236 if (ad->argName == NULL)
1237 ExitArgError("Unrecognized argument", argName);
1239 } else if (ch == '@') {
1240 /* Indirection file */
1241 ad = &argDescriptorIndirection;
1244 /* Positional argument */
1245 ad = &argDescriptors[posarg++];
1246 strcpy(argName, ad->argName);
1249 if (ad->argType == ArgTrue) {
1250 *(Boolean *) ad->argLoc = TRUE;
1253 if (ad->argType == ArgFalse) {
1254 *(Boolean *) ad->argLoc = FALSE;
1258 while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);
1259 if (ch == NULLCHAR || ch == '\n') {
1260 ExitArgError("No value provided for argument", argName);
1264 // Quoting with { }. No characters have to (or can) be escaped.
1265 // Thus the string cannot contain a '}' character.
1285 } else if (ch == '\'' || ch == '"') {
1286 // Quoting with ' ' or " ", with \ as escape character.
1287 // Inconvenient for long strings that may contain Windows filenames.
1313 if (ad->argType == ArgFilename
1314 || ad->argType == ArgSettingsFilename) {
1320 ExitArgError("Incomplete \\ escape in value for", argName);
1344 for (i = 0; i < 3; i++) {
1345 if (ch >= '0' && ch <= '7') {
1346 octval = octval*8 + (ch - '0');
1353 *q++ = (char) octval;
1364 while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {
1371 switch (ad->argType) {
1373 *(int *) ad->argLoc = atoi(argValue);
1377 *(float *) ad->argLoc = (float) atof(argValue);
1382 *(char **) ad->argLoc = strdup(argValue);
1385 case ArgSettingsFilename:
1387 char fullname[MSG_SIZ];
1388 if (ParseSettingsFile(argValue, fullname)) {
1389 if (ad->argLoc != NULL) {
1390 *(char **) ad->argLoc = strdup(fullname);
1393 if (ad->argLoc != NULL) {
1395 ExitArgError("Failed to open indirection file", argValue);
1402 switch (argValue[0]) {
1405 *(Boolean *) ad->argLoc = TRUE;
1409 *(Boolean *) ad->argLoc = FALSE;
1412 ExitArgError("Unrecognized boolean argument value", argValue);
1418 *(COLORREF *)ad->argLoc = ParseColorName(argValue);
1422 ColorClass cc = (ColorClass)ad->argLoc;
1423 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);
1428 *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);
1432 ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);
1435 case ArgCommSettings:
1436 ParseCommSettings(argValue, &dcb);
1440 ExitArgError("Unrecognized argument", argValue);
1447 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
1449 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
1450 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
1453 lf->lfEscapement = 0;
1454 lf->lfOrientation = 0;
1455 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
1456 lf->lfItalic = mfp->italic;
1457 lf->lfUnderline = mfp->underline;
1458 lf->lfStrikeOut = mfp->strikeout;
1459 lf->lfCharSet = DEFAULT_CHARSET;
1460 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
1461 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
1462 lf->lfQuality = DEFAULT_QUALITY;
1463 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
1464 strcpy(lf->lfFaceName, mfp->faceName);
1468 CreateFontInMF(MyFont *mf)
1470 LFfromMFP(&mf->lf, &mf->mfp);
1471 if (mf->hf) DeleteObject(mf->hf);
1472 mf->hf = CreateFontIndirect(&mf->lf);
1476 SetDefaultTextAttribs()
1479 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1480 ParseAttribs(&textAttribs[cc].color,
1481 &textAttribs[cc].effects,
1482 defaultTextAttribs[cc]);
1491 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1492 textAttribs[cc].sound.name = strdup("");
1493 textAttribs[cc].sound.data = NULL;
1495 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1496 sounds[sc].name = strdup("");
1497 sounds[sc].data = NULL;
1499 sounds[(int)SoundBell].name = strdup(SOUND_BELL);
1507 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1508 MyLoadSound(&textAttribs[cc].sound);
1510 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1511 MyLoadSound(&sounds[sc]);
1516 InitAppData(LPSTR lpCmdLine)
1519 char buf[ARG_MAX], currDir[MSG_SIZ];
1522 programName = szAppName;
1524 /* Initialize to defaults */
1525 lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);
1526 darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);
1527 whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);
1528 blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);
1529 highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);
1530 premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);
1531 consoleBackgroundColor = ParseColorName(COLOR_BKGD);
1532 SetDefaultTextAttribs();
1534 appData.movesPerSession = MOVES_PER_SESSION;
1535 appData.initString = INIT_STRING;
1536 appData.secondInitString = INIT_STRING;
1537 appData.firstComputerString = COMPUTER_STRING;
1538 appData.secondComputerString = COMPUTER_STRING;
1539 appData.firstChessProgram = FIRST_CHESS_PROGRAM;
1540 appData.secondChessProgram = SECOND_CHESS_PROGRAM;
1541 appData.firstPlaysBlack = FALSE;
1542 appData.noChessProgram = FALSE;
1543 chessProgram = FALSE;
1544 appData.firstHost = FIRST_HOST;
1545 appData.secondHost = SECOND_HOST;
1546 appData.firstDirectory = FIRST_DIRECTORY;
1547 appData.secondDirectory = SECOND_DIRECTORY;
1548 appData.bitmapDirectory = "";
1549 appData.remoteShell = REMOTE_SHELL;
1550 appData.remoteUser = "";
1551 appData.timeDelay = TIME_DELAY;
1552 appData.timeControl = TIME_CONTROL;
1553 appData.timeIncrement = TIME_INCREMENT;
1554 appData.icsActive = FALSE;
1555 appData.icsHost = "";
1556 appData.icsPort = ICS_PORT;
1557 appData.icsCommPort = ICS_COMM_PORT;
1558 appData.icsLogon = ICS_LOGON;
1559 appData.icsHelper = "";
1560 appData.useTelnet = FALSE;
1561 appData.telnetProgram = TELNET_PROGRAM;
1562 appData.gateway = "";
1563 appData.loadGameFile = "";
1564 appData.loadGameIndex = 0;
1565 appData.saveGameFile = "";
1566 appData.autoSaveGames = FALSE;
1567 appData.loadPositionFile = "";
1568 appData.loadPositionIndex = 1;
1569 appData.savePositionFile = "";
1570 appData.matchMode = FALSE;
1571 appData.matchGames = 0;
1572 appData.monoMode = FALSE;
1573 appData.debugMode = FALSE;
1574 appData.clockMode = TRUE;
1575 boardSize = (BoardSize) -1; /* determine by screen size */
1576 appData.Iconic = FALSE; /*unused*/
1577 appData.searchTime = "";
1578 appData.searchDepth = 0;
1579 appData.showCoords = FALSE;
1580 appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/
1581 appData.autoCallFlag = FALSE;
1582 appData.flipView = FALSE;
1583 appData.autoFlipView = TRUE;
1584 appData.cmailGameName = "";
1585 appData.alwaysPromoteToQueen = FALSE;
1586 appData.oldSaveStyle = FALSE;
1587 appData.quietPlay = FALSE;
1588 appData.showThinking = FALSE;
1589 appData.ponderNextMove = TRUE;
1590 appData.periodicUpdates = TRUE;
1591 appData.popupExitMessage = TRUE;
1592 appData.popupMoveErrors = FALSE;
1593 appData.autoObserve = FALSE;
1594 appData.autoComment = FALSE;
1595 appData.animate = TRUE;
1596 appData.animSpeed = 10;
1597 appData.animateDragging = TRUE;
1598 appData.highlightLastMove = TRUE;
1599 appData.getMoveList = TRUE;
1600 appData.testLegality = TRUE;
1601 appData.premove = TRUE;
1602 appData.premoveWhite = FALSE;
1603 appData.premoveWhiteText = "";
1604 appData.premoveBlack = FALSE;
1605 appData.premoveBlackText = "";
1606 appData.icsAlarm = TRUE;
1607 appData.icsAlarmTime = 5000;
1608 appData.autoRaiseBoard = TRUE;
1609 appData.localLineEditing = TRUE;
1610 appData.colorize = TRUE;
1611 appData.reuseFirst = TRUE;
1612 appData.reuseSecond = TRUE;
1613 appData.blindfold = FALSE;
1614 appData.icsEngineAnalyze = FALSE;
1615 dcb.DCBlength = sizeof(DCB);
1616 dcb.BaudRate = 9600;
1618 dcb.fParity = FALSE;
1619 dcb.fOutxCtsFlow = FALSE;
1620 dcb.fOutxDsrFlow = FALSE;
1621 dcb.fDtrControl = DTR_CONTROL_ENABLE;
1622 dcb.fDsrSensitivity = FALSE;
1623 dcb.fTXContinueOnXoff = TRUE;
1627 dcb.fRtsControl = RTS_CONTROL_ENABLE;
1628 dcb.fAbortOnError = FALSE;
1629 /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */
1630 #if (defined(_MSC_VER) && _MSC_VER <= 1200)
1631 //dcb.wReserved = 0;
1636 dcb.Parity = SPACEPARITY;
1637 dcb.StopBits = ONESTOPBIT;
1638 settingsFileName = SETTINGS_FILE;
1639 saveSettingsOnExit = TRUE;
1640 boardX = CW_USEDEFAULT;
1641 boardY = CW_USEDEFAULT;
1642 consoleX = CW_USEDEFAULT;
1643 consoleY = CW_USEDEFAULT;
1644 consoleW = CW_USEDEFAULT;
1645 consoleH = CW_USEDEFAULT;
1646 analysisX = CW_USEDEFAULT;
1647 analysisY = CW_USEDEFAULT;
1648 analysisW = CW_USEDEFAULT;
1649 analysisH = CW_USEDEFAULT;
1650 commentX = CW_USEDEFAULT;
1651 commentY = CW_USEDEFAULT;
1652 commentW = CW_USEDEFAULT;
1653 commentH = CW_USEDEFAULT;
1654 editTagsX = CW_USEDEFAULT;
1655 editTagsY = CW_USEDEFAULT;
1656 editTagsW = CW_USEDEFAULT;
1657 editTagsH = CW_USEDEFAULT;
1658 gameListX = CW_USEDEFAULT;
1659 gameListY = CW_USEDEFAULT;
1660 gameListW = CW_USEDEFAULT;
1661 gameListH = CW_USEDEFAULT;
1662 icsTextMenuString = ICS_TEXT_MENU_DEFAULT;
1663 icsNames = ICS_NAMES;
1664 firstChessProgramNames = FCP_NAMES;
1665 secondChessProgramNames = SCP_NAMES;
1666 appData.initialMode = "";
1667 appData.variant = "normal";
1668 appData.firstProtocolVersion = PROTOVER;
1669 appData.secondProtocolVersion = PROTOVER;
1670 appData.showButtonBar = TRUE;
1672 appData.zippyTalk = ZIPPY_TALK;
1673 appData.zippyPlay = ZIPPY_PLAY;
1674 appData.zippyLines = ZIPPY_LINES;
1675 appData.zippyPinhead = ZIPPY_PINHEAD;
1676 appData.zippyPassword = ZIPPY_PASSWORD;
1677 appData.zippyPassword2 = ZIPPY_PASSWORD2;
1678 appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;
1679 appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;
1680 appData.zippyUseI = ZIPPY_USE_I;
1681 appData.zippyBughouse = ZIPPY_BUGHOUSE;
1682 appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;
1683 appData.zippyGameEnd = ZIPPY_GAME_END;
1684 appData.zippyGameStart = ZIPPY_GAME_START;
1685 appData.zippyAdjourn = ZIPPY_ADJOURN;
1686 appData.zippyAbort = ZIPPY_ABORT;
1687 appData.zippyVariants = ZIPPY_VARIANTS;
1688 appData.zippyMaxGames = ZIPPY_MAX_GAMES;
1689 appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;
1692 /* Point font array elements to structures and
1693 parse default font names */
1694 for (i=0; i<NUM_FONTS; i++) {
1695 for (j=0; j<NUM_SIZES; j++) {
1696 font[j][i] = &fontRec[j][i];
1697 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
1701 /* Parse default settings file if any */
1702 if (ParseSettingsFile(settingsFileName, buf)) {
1703 settingsFileName = strdup(buf);
1706 /* Parse command line */
1707 ParseArgs(StringGet, &lpCmdLine);
1709 /* Propagate options that affect others */
1710 if (appData.matchMode || appData.matchGames) chessProgram = TRUE;
1711 if (appData.icsActive || appData.noChessProgram) {
1712 chessProgram = FALSE; /* not local chess program mode */
1715 /* Open startup dialog if needed */
1716 if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||
1717 (appData.icsActive && *appData.icsHost == NULLCHAR) ||
1718 (chessProgram && (*appData.firstChessProgram == NULLCHAR ||
1719 *appData.secondChessProgram == NULLCHAR))) {
1722 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
1723 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
1724 FreeProcInstance(lpProc);
1727 /* Make sure save files land in the right (?) directory */
1728 if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {
1729 appData.saveGameFile = strdup(buf);
1731 if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {
1732 appData.savePositionFile = strdup(buf);
1735 /* Finish initialization for fonts and sounds */
1736 for (i=0; i<NUM_FONTS; i++) {
1737 for (j=0; j<NUM_SIZES; j++) {
1738 CreateFontInMF(font[j][i]);
1741 /* xboard, and older WinBoards, controlled the move sound with the
1742 appData.ringBellAfterMoves option. In the current WinBoard, we
1743 always turn the option on (so that the backend will call us),
1744 then let the user turn the sound off by setting it to silence if
1745 desired. To accommodate old winboard.ini files saved by old
1746 versions of WinBoard, we also turn off the sound if the option
1747 was initially set to false. */
1748 if (!appData.ringBellAfterMoves) {
1749 sounds[(int)SoundMove].name = strdup("");
1750 appData.ringBellAfterMoves = TRUE;
1752 GetCurrentDirectory(MSG_SIZ, currDir);
1753 SetCurrentDirectory(installDir);
1755 SetCurrentDirectory(currDir);
1757 p = icsTextMenuString;
1759 FILE* f = fopen(p + 1, "r");
1761 DisplayFatalError(p + 1, errno, 2);
1764 i = fread(buf, 1, sizeof(buf)-1, f);
1769 ParseIcsTextMenu(strdup(p));
1776 HMENU hmenu = GetMenu(hwndMain);
1778 (void) EnableMenuItem(hmenu, IDM_CommPort,
1779 MF_BYCOMMAND|((appData.icsActive &&
1780 *appData.icsCommPort != NULLCHAR) ?
1781 MF_ENABLED : MF_GRAYED));
1782 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
1783 MF_BYCOMMAND|(saveSettingsOnExit ?
1784 MF_CHECKED : MF_UNCHECKED));
1789 SaveSettings(char* name)
1796 if (!hwndMain) return;
1798 GetCurrentDirectory(MSG_SIZ, dir);
1799 SetCurrentDirectory(installDir);
1800 f = fopen(name, "w");
1801 SetCurrentDirectory(dir);
1803 DisplayError(name, errno);
1807 fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);
1809 fprintf(f, "; You can edit the values of options that are already set in this file,\n");
1810 fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");
1811 fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");
1814 wp.length = sizeof(WINDOWPLACEMENT);
1815 GetWindowPlacement(hwndMain, &wp);
1816 boardX = wp.rcNormalPosition.left;
1817 boardY = wp.rcNormalPosition.top;
1820 GetWindowPlacement(hwndConsole, &wp);
1821 consoleX = wp.rcNormalPosition.left;
1822 consoleY = wp.rcNormalPosition.top;
1823 consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1824 consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1827 if (analysisDialog) {
1828 GetWindowPlacement(analysisDialog, &wp);
1829 analysisX = wp.rcNormalPosition.left;
1830 analysisY = wp.rcNormalPosition.top;
1831 analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1832 analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1835 if (commentDialog) {
1836 GetWindowPlacement(commentDialog, &wp);
1837 commentX = wp.rcNormalPosition.left;
1838 commentY = wp.rcNormalPosition.top;
1839 commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1840 commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1843 if (editTagsDialog) {
1844 GetWindowPlacement(editTagsDialog, &wp);
1845 editTagsX = wp.rcNormalPosition.left;
1846 editTagsY = wp.rcNormalPosition.top;
1847 editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1848 editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1851 if (gameListDialog) {
1852 GetWindowPlacement(gameListDialog, &wp);
1853 gameListX = wp.rcNormalPosition.left;
1854 gameListY = wp.rcNormalPosition.top;
1855 gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1856 gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1859 for (ad = argDescriptors; ad->argName != NULL; ad++) {
1860 if (!ad->save) continue;
1861 switch (ad->argType) {
1864 char *p = *(char **)ad->argLoc;
1865 if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {
1866 /* Quote multiline values or \-containing values
1867 with { } if possible */
1868 fprintf(f, "/%s={%s}\n", ad->argName, p);
1870 /* Else quote with " " */
1871 fprintf(f, "/%s=\"", ad->argName);
1873 if (*p == '\n') fprintf(f, "\n");
1874 else if (*p == '\r') fprintf(f, "\\r");
1875 else if (*p == '\t') fprintf(f, "\\t");
1876 else if (*p == '\b') fprintf(f, "\\b");
1877 else if (*p == '\f') fprintf(f, "\\f");
1878 else if (*p < ' ') fprintf(f, "\\%03o", *p);
1879 else if (*p == '\"') fprintf(f, "\\\"");
1880 else if (*p == '\\') fprintf(f, "\\\\");
1889 fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);
1892 fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);
1895 fprintf(f, "/%s=%s\n", ad->argName,
1896 (*(Boolean *)ad->argLoc) ? "true" : "false");
1899 if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1902 if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1906 COLORREF color = *(COLORREF *)ad->argLoc;
1907 fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName,
1908 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1913 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
1914 fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,
1915 (ta->effects & CFE_BOLD) ? "b" : "",
1916 (ta->effects & CFE_ITALIC) ? "i" : "",
1917 (ta->effects & CFE_UNDERLINE) ? "u" : "",
1918 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
1919 (ta->effects) ? " " : "",
1920 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
1924 if (strchr(*(char **)ad->argLoc, '\"')) {
1925 fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);
1927 fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);
1931 fprintf(f, "/%s=%s\n", ad->argName,
1932 sizeInfo[*(BoardSize *)ad->argLoc].name);
1937 for (bs=0; bs<NUM_SIZES; bs++) {
1938 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
1939 fprintf(f, "/size=%s ", sizeInfo[bs].name);
1940 fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",
1941 ad->argName, mfp->faceName, mfp->pointSize,
1942 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
1943 mfp->bold ? "b" : "",
1944 mfp->italic ? "i" : "",
1945 mfp->underline ? "u" : "",
1946 mfp->strikeout ? "s" : "");
1950 case ArgCommSettings:
1951 PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);
1959 /*---------------------------------------------------------------------------*\
1961 * GDI board drawing routines
1963 \*---------------------------------------------------------------------------*/
1966 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
1970 sprintf(name, "%s%d%s", piece, squareSize, suffix);
1971 if (gameInfo.event &&
1972 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
1973 strcmp(name, "k80s") == 0) {
1974 strcpy(name, "tim");
1976 return LoadBitmap(hinst, name);
1980 /* Insert a color into the program's logical palette
1981 structure. This code assumes the given color is
1982 the result of the RGB or PALETTERGB macro, and it
1983 knows how those macros work (which is documented).
1986 InsertInPalette(COLORREF color)
1988 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
1990 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
1991 DisplayFatalError("Too many colors", 0, 1);
1992 pLogPal->palNumEntries--;
1996 pe->peFlags = (char) 0;
1997 pe->peRed = (char) (0xFF & color);
1998 pe->peGreen = (char) (0xFF & (color >> 8));
1999 pe->peBlue = (char) (0xFF & (color >> 16));
2007 if (pLogPal == NULL) {
2008 /* Allocate enough memory for a logical palette with
2009 * PALETTESIZE entries and set the size and version fields
2010 * of the logical palette structure.
2012 pLogPal = (NPLOGPALETTE)
2013 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
2014 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
2015 pLogPal->palVersion = 0x300;
2017 pLogPal->palNumEntries = 0;
2019 InsertInPalette(lightSquareColor);
2020 InsertInPalette(darkSquareColor);
2021 InsertInPalette(whitePieceColor);
2022 InsertInPalette(blackPieceColor);
2023 InsertInPalette(highlightSquareColor);
2024 InsertInPalette(premoveHighlightColor);
2026 /* create a logical color palette according the information
2027 * in the LOGPALETTE structure.
2029 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
2031 lightSquareBrush = CreateSolidBrush(lightSquareColor);
2032 darkSquareBrush = CreateSolidBrush(darkSquareColor);
2033 whitePieceBrush = CreateSolidBrush(whitePieceColor);
2034 blackPieceBrush = CreateSolidBrush(blackPieceColor);
2035 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
2040 BoardWidth(int boardSize)
2042 return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +
2043 BOARD_SIZE * sizeInfo[boardSize].squareSize;
2046 /* Respond to board resize by dragging edge */
2048 ResizeBoard(int newSizeX, int newSizeY, int flags)
2050 BoardSize newSize = NUM_SIZES - 1;
2051 static int recurse = 0;
2052 if (IsIconic(hwndMain)) return;
2053 if (recurse > 0) return;
2055 while (newSize > 0 &&
2056 (newSizeX < sizeInfo[newSize].cliWidth ||
2057 newSizeY < sizeInfo[newSize].cliHeight)) {
2060 boardSize = newSize;
2061 InitDrawingSizes(boardSize, flags);
2068 InitDrawingSizes(BoardSize boardSize, int flags)
2072 static int oldBoardSize = -1, oldTinyLayout = 0;
2074 SIZE clockSize, messageSize;
2078 HMENU hmenu = GetMenu(hwndMain);
2083 tinyLayout = sizeInfo[boardSize].tinyLayout;
2084 smallLayout = sizeInfo[boardSize].smallLayout;
2085 squareSize = sizeInfo[boardSize].squareSize;
2086 lineGap = sizeInfo[boardSize].lineGap;
2088 if (tinyLayout != oldTinyLayout) {
2089 long style = GetWindowLong(hwndMain, GWL_STYLE);
2091 style &= ~WS_SYSMENU;
2092 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
2093 "&Minimize\tCtrl+F4");
2095 style |= WS_SYSMENU;
2096 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
2098 SetWindowLong(hwndMain, GWL_STYLE, style);
2100 for (i=0; menuBarText[tinyLayout][i]; i++) {
2101 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
2102 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
2104 DrawMenuBar(hwndMain);
2107 boardWidth = BoardWidth(boardSize);
2109 /* Get text area sizes */
2110 hdc = GetDC(hwndMain);
2111 if (appData.clockMode) {
2112 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
2114 sprintf(buf, "White");
2116 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
2117 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
2118 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2119 str = "We only care about the height here";
2120 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
2121 SelectObject(hdc, oldFont);
2122 ReleaseDC(hwndMain, hdc);
2124 /* Compute where everything goes */
2125 whiteRect.left = OUTER_MARGIN;
2126 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
2127 whiteRect.top = OUTER_MARGIN;
2128 whiteRect.bottom = whiteRect.top + clockSize.cy;
2130 blackRect.left = whiteRect.right + INNER_MARGIN;
2131 blackRect.right = blackRect.left + boardWidth/2 - 1;
2132 blackRect.top = whiteRect.top;
2133 blackRect.bottom = whiteRect.bottom;
2135 messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;
2136 if (appData.showButtonBar) {
2137 messageRect.right = blackRect.right
2138 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
2140 messageRect.right = blackRect.right;
2142 messageRect.top = whiteRect.bottom + INNER_MARGIN;
2143 messageRect.bottom = messageRect.top + messageSize.cy;
2145 boardRect.left = whiteRect.left;
2146 boardRect.right = boardRect.left + boardWidth;
2147 boardRect.top = messageRect.bottom + INNER_MARGIN;
2148 boardRect.bottom = boardRect.top + boardWidth;
2150 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
2151 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
2152 winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
2153 winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
2154 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
2155 GetWindowRect(hwndMain, &wrect);
2156 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2157 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2158 /* compensate if menu bar wrapped */
2159 GetClientRect(hwndMain, &crect);
2160 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
2164 SetWindowPos(hwndMain, NULL,
2165 wrect.right - winWidth, wrect.bottom - winHeight,
2166 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2171 SetWindowPos(hwndMain, NULL,
2172 wrect.left, wrect.bottom - winHeight,
2173 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2176 case WMSZ_BOTTOMLEFT:
2178 SetWindowPos(hwndMain, NULL,
2179 wrect.right - winWidth, wrect.top,
2180 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2183 case WMSZ_BOTTOMRIGHT:
2187 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2188 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2193 for (i = 0; i < N_BUTTONS; i++) {
2194 if (buttonDesc[i].hwnd != NULL) {
2195 DestroyWindow(buttonDesc[i].hwnd);
2196 buttonDesc[i].hwnd = NULL;
2198 if (appData.showButtonBar) {
2199 buttonDesc[i].hwnd =
2200 CreateWindow("BUTTON", buttonDesc[i].label,
2201 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
2202 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
2203 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
2204 (HMENU) buttonDesc[i].id,
2205 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
2207 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
2208 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
2209 MAKELPARAM(FALSE, 0));
2211 if (buttonDesc[i].id == IDM_Pause)
2212 hwndPause = buttonDesc[i].hwnd;
2213 buttonDesc[i].wndproc = (WNDPROC)
2214 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
2217 if (gridPen != NULL) DeleteObject(gridPen);
2218 if (highlightPen != NULL) DeleteObject(highlightPen);
2219 if (premovePen != NULL) DeleteObject(premovePen);
2221 logbrush.lbStyle = BS_SOLID;
2222 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
2224 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2225 lineGap, &logbrush, 0, NULL);
2226 logbrush.lbColor = highlightSquareColor;
2228 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2229 lineGap, &logbrush, 0, NULL);
2231 logbrush.lbColor = premoveHighlightColor;
2233 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2234 lineGap, &logbrush, 0, NULL);
2236 for (i = 0; i < BOARD_SIZE + 1; i++) {
2237 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
2238 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;
2239 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
2240 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
2241 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
2242 BOARD_SIZE * (squareSize + lineGap);
2243 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =
2244 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +
2245 lineGap / 2 + (i * (squareSize + lineGap));
2246 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =
2247 boardRect.top + BOARD_SIZE * (squareSize + lineGap);
2248 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
2252 if (boardSize == oldBoardSize) return;
2253 oldBoardSize = boardSize;
2254 oldTinyLayout = tinyLayout;
2256 /* Load piece bitmaps for this board size */
2257 for (i=0; i<=2; i++) {
2258 for (piece = WhitePawn;
2259 (int) piece <= (int) WhiteKing;
2260 piece = (ChessSquare) ((int) piece + 1)) {
2261 if (pieceBitmap[i][piece] != NULL)
2262 DeleteObject(pieceBitmap[i][piece]);
2266 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
2267 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
2268 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
2269 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
2270 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
2271 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
2272 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
2273 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
2274 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
2275 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
2276 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
2277 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
2278 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
2279 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
2280 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
2281 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
2282 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
2283 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
2288 PieceBitmap(ChessSquare p, int kind)
2290 if ((int) p >= (int) BlackPawn)
2291 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
2293 return pieceBitmap[kind][(int) p];
2296 /***************************************************************/
2298 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
2299 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
2301 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
2302 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
2306 SquareToPos(int row, int column, int * x, int * y)
2309 *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
2310 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
2312 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
2313 *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
2318 DrawCoordsOnDC(HDC hdc)
2320 static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};
2321 static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};
2322 char str[2] = { NULLCHAR, NULLCHAR };
2323 int oldMode, oldAlign, x, y, start, i;
2327 if (!appData.showCoords)
2330 start = flipView ? 0 : 8;
2332 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
2333 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
2334 oldAlign = GetTextAlign(hdc);
2335 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
2337 y = boardRect.top + lineGap;
2338 x = boardRect.left + lineGap;
2340 SetTextAlign(hdc, TA_LEFT|TA_TOP);
2341 for (i = 0; i < 8; i++) {
2342 str[0] = files[start + i];
2343 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
2344 y += squareSize + lineGap;
2347 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
2348 for (i = 0; i < 8; i++) {
2349 str[0] = ranks[start + i];
2350 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
2351 x += squareSize + lineGap;
2354 SelectObject(hdc, oldBrush);
2355 SetBkMode(hdc, oldMode);
2356 SetTextAlign(hdc, oldAlign);
2357 SelectObject(hdc, oldFont);
2361 DrawGridOnDC(HDC hdc)
2366 oldPen = SelectObject(hdc, gridPen);
2367 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);
2368 SelectObject(hdc, oldPen);
2372 #define HIGHLIGHT_PEN 0
2373 #define PREMOVE_PEN 1
2376 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
2380 if (lineGap == 0) return;
2382 x1 = boardRect.left +
2383 lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);
2384 y1 = boardRect.top +
2385 lineGap/2 + y * (squareSize + lineGap);
2387 x1 = boardRect.left +
2388 lineGap/2 + x * (squareSize + lineGap);
2389 y1 = boardRect.top +
2390 lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);
2392 hPen = pen ? premovePen : highlightPen;
2393 oldPen = SelectObject(hdc, on ? hPen : gridPen);
2394 MoveToEx(hdc, x1, y1, NULL);
2395 LineTo(hdc, x1 + squareSize + lineGap, y1);
2396 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
2397 LineTo(hdc, x1, y1 + squareSize + lineGap);
2398 LineTo(hdc, x1, y1);
2399 SelectObject(hdc, oldPen);
2403 DrawHighlightsOnDC(HDC hdc)
2406 for (i=0; i<2; i++) {
2407 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
2408 DrawHighlightOnDC(hdc, TRUE,
2409 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
2412 for (i=0; i<2; i++) {
2413 if (premoveHighlightInfo.sq[i].x >= 0 &&
2414 premoveHighlightInfo.sq[i].y >= 0) {
2415 DrawHighlightOnDC(hdc, TRUE,
2416 premoveHighlightInfo.sq[i].x,
2417 premoveHighlightInfo.sq[i].y,
2423 /* Note: sqcolor is used only in monoMode */
2424 /* Note that this code is largely duplicated in woptions.c,
2425 function DrawSampleSquare, so that needs to be updated too */
2427 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
2432 if (appData.blindfold) return;
2434 if (appData.monoMode) {
2435 SelectObject(tmphdc, PieceBitmap(piece,
2436 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
2437 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
2438 sqcolor ? SRCCOPY : NOTSRCCOPY);
2441 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
2442 oldBrush = SelectObject(hdc, whitePieceBrush);
2443 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2445 /* Use black piece color for outline of white pieces */
2446 /* Not sure this looks really good (though xboard does it).
2447 Maybe better to have another selectable color, default black */
2448 SelectObject(hdc, blackPieceBrush); /* could have own brush */
2449 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2450 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2452 /* Use black for outline of white pieces */
2453 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2454 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);
2458 /* Use white piece color for details of black pieces */
2459 /* Requires filled-in solid bitmaps (BLACK_PIECE class); the
2460 WHITE_PIECE ones aren't always the right shape. */
2461 /* Not sure this looks really good (though xboard does it).
2462 Maybe better to have another selectable color, default medium gray? */
2463 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));
2464 oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */
2465 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2466 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2467 SelectObject(hdc, blackPieceBrush);
2468 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2470 /* Use square color for details of black pieces */
2471 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2472 oldBrush = SelectObject(hdc, blackPieceBrush);
2473 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2476 SelectObject(hdc, oldBrush);
2477 SelectObject(tmphdc, oldBitmap);
2482 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
2484 int row, column, x, y, square_color, piece_color;
2488 for (row = 0; row < BOARD_SIZE; row++) {
2489 for (column = 0; column < BOARD_SIZE; column++) {
2491 SquareToPos(row, column, &x, &y);
2493 piece = board[row][column];
2495 square_color = ((column + row) % 2) == 1;
2496 piece_color = (int) piece < (int) BlackPawn;
2498 if (appData.monoMode) {
2499 if (piece == EmptySquare) {
2500 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
2501 square_color ? WHITENESS : BLACKNESS);
2503 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
2506 oldBrush = SelectObject(hdc, square_color ?
2507 lightSquareBrush : darkSquareBrush);
2508 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
2509 SelectObject(hdc, oldBrush);
2510 if (piece != EmptySquare)
2511 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
2517 #define MAX_CLIPS 200 /* more than enough */
2520 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
2522 static Board lastReq, lastDrawn;
2523 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
2524 static int lastDrawnFlipView = 0;
2525 static int lastReqValid = 0, lastDrawnValid = 0;
2526 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
2529 HBITMAP bufferBitmap;
2532 HRGN clips[MAX_CLIPS];
2533 ChessSquare dragged_piece = EmptySquare;
2535 /* I'm undecided on this - this function figures out whether a full
2536 * repaint is necessary on its own, so there's no real reason to have the
2537 * caller tell it that. I think this can safely be set to FALSE - but
2538 * if we trust the callers not to request full repaints unnessesarily, then
2539 * we could skip some clipping work. In other words, only request a full
2540 * redraw when the majority of pieces have changed positions (ie. flip,
2541 * gamestart and similar) --Hawk
2543 Boolean fullrepaint = repaint;
2545 if (board == NULL) {
2546 if (!lastReqValid) {
2551 CopyBoard(lastReq, board);
2559 if (IsIconic(hwndMain)) {
2564 hdc = GetDC(hwndMain);
2565 if (!appData.monoMode) {
2566 SelectPalette(hdc, hPal, FALSE);
2567 RealizePalette(hdc);
2575 fprintf(debugFP, "*******************************\n"
2577 "dragInfo.from (%d,%d)\n"
2578 "dragInfo.start (%d,%d)\n"
2579 "dragInfo.pos (%d,%d)\n"
2580 "dragInfo.lastpos (%d,%d)\n",
2581 repaint ? "TRUE" : "FALSE",
2582 dragInfo.from.x, dragInfo.from.y,
2583 dragInfo.start.x, dragInfo.start.y,
2584 dragInfo.pos.x, dragInfo.pos.y,
2585 dragInfo.lastpos.x, dragInfo.lastpos.y);
2586 fprintf(debugFP, "prev: ");
2587 for (row = 0; row < 8; row++) {
2588 for (column = 0; column < 8; column++) {
2589 fprintf(debugFP, "%d ", lastDrawn[row][column]);
2592 fprintf(debugFP, "\n");
2593 fprintf(debugFP, "board: ");
2594 for (row = 0; row < 8; row++) {
2595 for (column = 0; column < 8; column++) {
2596 fprintf(debugFP, "%d ", board[row][column]);
2599 fprintf(debugFP, "\n");
2603 /* Create some work-DCs */
2604 hdcmem = CreateCompatibleDC(hdc);
2605 tmphdc = CreateCompatibleDC(hdc);
2607 /* Figure out which squares need updating by comparing the
2608 * newest board with the last drawn board and checking if
2609 * flipping has changed.
2611 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
2612 for (row = 0; row < 8; row++) {
2613 for (column = 0; column < 8; column++) {
2614 if (lastDrawn[row][column] != board[row][column]) {
2615 SquareToPos(row, column, &x, &y);
2616 clips[num_clips++] =
2617 CreateRectRgn(x, y, x + squareSize, y + squareSize);
2621 for (i=0; i<2; i++) {
2622 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
2623 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
2624 if (lastDrawnHighlight.sq[i].x >= 0 &&
2625 lastDrawnHighlight.sq[i].y >= 0) {
2626 SquareToPos(lastDrawnHighlight.sq[i].y,
2627 lastDrawnHighlight.sq[i].x, &x, &y);
2628 clips[num_clips++] =
2629 CreateRectRgn(x - lineGap, y - lineGap,
2630 x + squareSize + lineGap, y + squareSize + lineGap);
2632 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
2633 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
2634 clips[num_clips++] =
2635 CreateRectRgn(x - lineGap, y - lineGap,
2636 x + squareSize + lineGap, y + squareSize + lineGap);
2640 for (i=0; i<2; i++) {
2641 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
2642 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
2643 if (lastDrawnPremove.sq[i].x >= 0 &&
2644 lastDrawnPremove.sq[i].y >= 0) {
2645 SquareToPos(lastDrawnPremove.sq[i].y,
2646 lastDrawnPremove.sq[i].x, &x, &y);
2647 clips[num_clips++] =
2648 CreateRectRgn(x - lineGap, y - lineGap,
2649 x + squareSize + lineGap, y + squareSize + lineGap);
2651 if (premoveHighlightInfo.sq[i].x >= 0 &&
2652 premoveHighlightInfo.sq[i].y >= 0) {
2653 SquareToPos(premoveHighlightInfo.sq[i].y,
2654 premoveHighlightInfo.sq[i].x, &x, &y);
2655 clips[num_clips++] =
2656 CreateRectRgn(x - lineGap, y - lineGap,
2657 x + squareSize + lineGap, y + squareSize + lineGap);
2665 /* Create a buffer bitmap - this is the actual bitmap
2666 * being written to. When all the work is done, we can
2667 * copy it to the real DC (the screen). This avoids
2668 * the problems with flickering.
2670 GetClientRect(hwndMain, &Rect);
2671 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
2672 Rect.bottom-Rect.top+1);
2673 oldBitmap = SelectObject(hdcmem, bufferBitmap);
2674 if (!appData.monoMode) {
2675 SelectPalette(hdcmem, hPal, FALSE);
2678 /* Create clips for dragging */
2680 if (dragInfo.from.x >= 0) {
2681 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
2682 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2684 if (dragInfo.start.x >= 0) {
2685 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
2686 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2688 if (dragInfo.pos.x >= 0) {
2689 x = dragInfo.pos.x - squareSize / 2;
2690 y = dragInfo.pos.y - squareSize / 2;
2691 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2693 if (dragInfo.lastpos.x >= 0) {
2694 x = dragInfo.lastpos.x - squareSize / 2;
2695 y = dragInfo.lastpos.y - squareSize / 2;
2696 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2700 /* If dragging is in progress, we temporarely remove the piece */
2701 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
2702 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
2703 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
2706 /* Are we animating a move?
2708 * - remove the piece from the board (temporarely)
2709 * - calculate the clipping region
2712 if (animInfo.piece != EmptySquare) {
2713 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
2714 x = boardRect.left + animInfo.lastpos.x;
2715 y = boardRect.top + animInfo.lastpos.y;
2716 x2 = boardRect.left + animInfo.pos.x;
2717 y2 = boardRect.top + animInfo.pos.y;
2718 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
2719 /* Slight kludge. The real problem is that after AnimateMove is
2720 done, the position on the screen does not match lastDrawn.
2721 This currently causes trouble only on e.p. captures in
2722 atomic, where the piece moves to an empty square and then
2723 explodes. The old and new positions both had an empty square
2724 at the destination, but animation has drawn a piece there and
2725 we have to remember to erase it. */
2726 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
2730 /* No clips? Make sure we have fullrepaint set to TRUE */
2734 /* Set clipping on the memory DC */
2736 SelectClipRgn(hdcmem, clips[0]);
2737 for (x = 1; x < num_clips; x++) {
2738 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
2739 abort(); // this should never ever happen!
2743 /* Do all the drawing to the memory DC */
2744 DrawGridOnDC(hdcmem);
2745 DrawHighlightsOnDC(hdcmem);
2746 DrawBoardOnDC(hdcmem, board, tmphdc);
2747 DrawCoordsOnDC(hdcmem);
2749 /* Put the dragged piece back into place and draw it */
2750 if (dragged_piece != EmptySquare) {
2751 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
2752 x = dragInfo.pos.x - squareSize / 2;
2753 y = dragInfo.pos.y - squareSize / 2;
2754 DrawPieceOnDC(hdcmem, dragged_piece,
2755 ((int) dragged_piece < (int) BlackPawn),
2756 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
2759 /* Put the animated piece back into place and draw it */
2760 if (animInfo.piece != EmptySquare) {
2761 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
2762 x = boardRect.left + animInfo.pos.x;
2763 y = boardRect.top + animInfo.pos.y;
2764 DrawPieceOnDC(hdcmem, animInfo.piece,
2765 ((int) animInfo.piece < (int) BlackPawn),
2766 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
2769 /* Release the bufferBitmap by selecting in the old bitmap
2770 * and delete the memory DC
2772 SelectObject(hdcmem, oldBitmap);
2775 /* Set clipping on the target DC */
2777 SelectClipRgn(hdc, clips[0]);
2778 for (x = 1; x < num_clips; x++) {
2779 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
2780 abort(); // this should never ever happen!
2784 /* Copy the new bitmap onto the screen in one go.
2785 * This way we avoid any flickering
2787 oldBitmap = SelectObject(tmphdc, bufferBitmap);
2788 BitBlt(hdc, boardRect.left, boardRect.top,
2789 boardRect.right - boardRect.left,
2790 boardRect.bottom - boardRect.top,
2791 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
2792 SelectObject(tmphdc, oldBitmap);
2794 /* Massive cleanup */
2795 for (x = 0; x < num_clips; x++)
2796 DeleteObject(clips[x]);
2799 DeleteObject(bufferBitmap);
2802 ReleaseDC(hwndMain, hdc);
2804 if (lastDrawnFlipView != flipView) {
2806 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
2808 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
2811 CopyBoard(lastDrawn, board);
2812 lastDrawnHighlight = highlightInfo;
2813 lastDrawnPremove = premoveHighlightInfo;
2814 lastDrawnFlipView = flipView;
2819 /*---------------------------------------------------------------------------*\
2820 | CLIENT PAINT PROCEDURE
2821 | This is the main event-handler for the WM_PAINT message.
2823 \*---------------------------------------------------------------------------*/
2825 PaintProc(HWND hwnd)
2831 if(hdc = BeginPaint(hwnd, &ps)) {
2832 if (IsIconic(hwnd)) {
2833 DrawIcon(hdc, 2, 2, iconCurrent);
2835 if (!appData.monoMode) {
2836 SelectPalette(hdc, hPal, FALSE);
2837 RealizePalette(hdc);
2839 HDCDrawPosition(hdc, 1, NULL);
2841 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2842 ExtTextOut(hdc, messageRect.left, messageRect.top,
2843 ETO_CLIPPED|ETO_OPAQUE,
2844 &messageRect, messageText, strlen(messageText), NULL);
2845 SelectObject(hdc, oldFont);
2846 DisplayBothClocks();
2856 * If the user selects on a border boundary, return -1; if off the board,
2857 * return -2. Otherwise map the event coordinate to the square.
2858 * The offset boardRect.left or boardRect.top must already have been
2859 * subtracted from x.
2862 EventToSquare(int x)
2869 if ((x % (squareSize + lineGap)) >= squareSize)
2871 x /= (squareSize + lineGap);
2872 if (x >= BOARD_SIZE)
2883 DropEnable dropEnables[] = {
2884 { 'P', DP_Pawn, "Pawn" },
2885 { 'N', DP_Knight, "Knight" },
2886 { 'B', DP_Bishop, "Bishop" },
2887 { 'R', DP_Rook, "Rook" },
2888 { 'Q', DP_Queen, "Queen" },
2892 SetupDropMenu(HMENU hmenu)
2894 int i, count, enable;
2896 extern char white_holding[], black_holding[];
2899 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
2900 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2901 dropEnables[i].piece);
2903 while (p && *p++ == dropEnables[i].piece) count++;
2904 sprintf(item, "%s %d", dropEnables[i].name, count);
2905 enable = count > 0 || !appData.testLegality
2906 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2907 && !appData.icsActive);
2908 ModifyMenu(hmenu, dropEnables[i].command,
2909 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
2910 dropEnables[i].command, item);
2914 static int fromX = -1, fromY = -1, toX, toY;
2916 /* Event handler for mouse messages */
2918 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2922 static int recursive = 0;
2924 BOOLEAN saveAnimate;
2925 static BOOLEAN sameAgain = FALSE;
2928 if (message == WM_MBUTTONUP) {
2929 /* Hideous kludge to fool TrackPopupMenu into paying attention
2930 to the middle button: we simulate pressing the left button too!
2932 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
2933 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
2939 pt.x = LOWORD(lParam);
2940 pt.y = HIWORD(lParam);
2941 x = EventToSquare(pt.x - boardRect.left);
2942 y = EventToSquare(pt.y - boardRect.top);
2943 if (!flipView && y >= 0) {
2944 y = BOARD_SIZE - 1 - y;
2946 if (flipView && x >= 0) {
2947 x = BOARD_SIZE - 1 - x;
2951 case WM_LBUTTONDOWN:
2955 /* Downclick vertically off board; check if on clock */
2956 if (PtInRect((LPRECT) &whiteRect, pt)) {
2957 if (gameMode == EditPosition) {
2958 SetWhiteToPlayEvent();
2959 } else if (gameMode == IcsPlayingBlack ||
2960 gameMode == MachinePlaysWhite) {
2963 } else if (PtInRect((LPRECT) &blackRect, pt)) {
2964 if (gameMode == EditPosition) {
2965 SetBlackToPlayEvent();
2966 } else if (gameMode == IcsPlayingWhite ||
2967 gameMode == MachinePlaysBlack) {
2971 if (!appData.highlightLastMove) {
2973 DrawPosition(FALSE, NULL);
2976 dragInfo.start.x = dragInfo.start.y = -1;
2977 dragInfo.from = dragInfo.start;
2979 } else if (x < 0 || y < 0) {
2981 } else if (fromX == x && fromY == y) {
2982 /* Downclick on same square again */
2984 DrawPosition(FALSE, NULL);
2986 } else if (fromX != -1) {
2987 /* Downclick on different square */
2988 ChessSquare pdown, pup;
2989 pdown = boards[currentMove][fromY][fromX];
2990 pup = boards[currentMove][y][x];
2991 if (gameMode == EditPosition ||
2992 !((WhitePawn <= pdown && pdown <= WhiteKing &&
2993 WhitePawn <= pup && pup <= WhiteKing) ||
2994 (BlackPawn <= pdown && pdown <= BlackKing &&
2995 BlackPawn <= pup && pup <= BlackKing))) {
2996 /* EditPosition, empty square, or different color piece;
2997 click-click move is possible */
3000 if (IsPromotion(fromX, fromY, toX, toY)) {
3001 if (appData.alwaysPromoteToQueen) {
3002 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3003 if (!appData.highlightLastMove) {
3005 DrawPosition(FALSE, NULL);
3008 SetHighlights(fromX, fromY, toX, toY);
3009 DrawPosition(FALSE, NULL);
3010 PromotionPopup(hwnd);
3012 } else { /* not a promotion */
3013 if (appData.animate || appData.highlightLastMove) {
3014 SetHighlights(fromX, fromY, toX, toY);
3018 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3019 if (appData.animate && !appData.highlightLastMove) {
3021 DrawPosition(FALSE, NULL);
3024 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3029 DrawPosition(FALSE, NULL);
3031 /* First downclick, or restart on a square with same color piece */
3032 if (!frozen && OKToStartUserMove(x, y)) {
3035 dragInfo.lastpos = pt;
3036 dragInfo.from.x = fromX;
3037 dragInfo.from.y = fromY;
3038 dragInfo.start = dragInfo.from;
3039 SetCapture(hwndMain);
3042 dragInfo.start.x = dragInfo.start.y = -1;
3043 dragInfo.from = dragInfo.start;
3049 if (fromX == -1) break;
3050 if (x == fromX && y == fromY) {
3051 dragInfo.from.x = dragInfo.from.y = -1;
3052 /* Upclick on same square */
3054 /* Clicked same square twice: abort click-click move */
3057 ClearPremoveHighlights();
3059 /* First square clicked: start click-click move */
3060 SetHighlights(fromX, fromY, -1, -1);
3062 DrawPosition(FALSE, NULL);
3063 } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {
3064 /* Errant click; ignore */
3067 /* Finish drag move */
3068 dragInfo.from.x = dragInfo.from.y = -1;
3071 saveAnimate = appData.animate; /* sorry, Hawk :) */
3072 appData.animate = appData.animate && !appData.animateDragging;
3073 if (IsPromotion(fromX, fromY, toX, toY)) {
3074 if (appData.alwaysPromoteToQueen) {
3075 UserMoveEvent(fromX, fromY, toX, toY, 'q');
3077 DrawPosition(FALSE, NULL);
3078 PromotionPopup(hwnd);
3081 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3083 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3084 appData.animate = saveAnimate;
3086 if (appData.highlightDragging && !appData.highlightLastMove) {
3089 if (appData.animate || appData.animateDragging ||
3090 appData.highlightDragging || gotPremove) {
3091 DrawPosition(FALSE, NULL);
3094 dragInfo.start.x = dragInfo.start.y = -1;
3095 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
3099 if ((appData.animateDragging || appData.highlightDragging)
3100 && (wParam & MK_LBUTTON)
3101 && dragInfo.from.x >= 0) {
3102 if (appData.animateDragging) {
3105 if (appData.highlightDragging) {
3106 SetHighlights(fromX, fromY, x, y);
3108 DrawPosition(FALSE, NULL);
3109 dragInfo.lastpos = dragInfo.pos;
3113 /* Mouse Wheel is being rolled forward
3114 * Play moves forward
3116 if ((short)HIWORD(wParam) > 0)
3117 if (forwardMostMove > 0 && currentMove != forwardMostMove)
3119 /* Mouse Wheel is being rolled backward
3120 * Play moves backward
3122 if ((short)HIWORD(wParam) < 0)
3123 if (currentMove > 0) BackwardEvent();
3125 case WM_MBUTTONDOWN:
3126 case WM_RBUTTONDOWN:
3130 dragInfo.pos.x = dragInfo.pos.y = -1;
3131 dragInfo.start.x = dragInfo.start.y = -1;
3132 dragInfo.from = dragInfo.start;
3133 dragInfo.lastpos = dragInfo.pos;
3134 if (appData.highlightDragging) {
3137 DrawPosition(TRUE, NULL);
3142 if (x < 0 || y < 0) break;
3145 if (message == WM_MBUTTONDOWN) {
3146 buttonCount = 3; /* even if system didn't think so */
3147 if (wParam & MK_SHIFT)
3148 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3150 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3151 } else { /* message == WM_RBUTTONDOWN */
3153 if (buttonCount == 3) {
3154 if (wParam & MK_SHIFT)
3155 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3157 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3159 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3162 /* Just have one menu, on the right button. Windows users don't
3163 think to try the middle one, and sometimes other software steals
3164 it, or it doesn't really exist. */
3165 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3169 case IcsPlayingWhite:
3170 case IcsPlayingBlack:
3172 case MachinePlaysWhite:
3173 case MachinePlaysBlack:
3174 if (appData.testLegality &&
3175 gameInfo.variant != VariantBughouse &&
3176 gameInfo.variant != VariantCrazyhouse) break;
3177 if (x < 0 || y < 0) break;
3180 hmenu = LoadMenu(hInst, "DropPieceMenu");
3181 SetupDropMenu(hmenu);
3182 MenuPopup(hwnd, pt, hmenu, -1);
3193 /* Preprocess messages for buttons in main window */
3195 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3197 int id = GetWindowLong(hwnd, GWL_ID);
3200 for (i=0; i<N_BUTTONS; i++) {
3201 if (buttonDesc[i].id == id) break;
3203 if (i == N_BUTTONS) return 0;
3209 dir = (wParam == VK_LEFT) ? -1 : 1;
3210 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
3217 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
3220 if (appData.icsActive) {
3221 if (GetKeyState(VK_SHIFT) < 0) {
3223 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3224 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3228 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3229 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3236 if (appData.icsActive) {
3237 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3238 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3240 SendMessage(h, WM_CHAR, wParam, lParam);
3242 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
3243 PopUpMoveDialog((char)wParam);
3249 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
3252 /* Process messages for Promotion dialog box */
3254 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
3259 case WM_INITDIALOG: /* message: initialize dialog box */
3260 /* Center the dialog over the application window */
3261 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
3262 ShowWindow(GetDlgItem(hDlg, PB_King),
3263 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3264 gameInfo.variant == VariantGiveaway) ?
3268 case WM_COMMAND: /* message: received a command */
3269 switch (LOWORD(wParam)) {
3271 EndDialog(hDlg, TRUE); /* Exit the dialog */
3273 DrawPosition(FALSE, NULL);
3293 EndDialog(hDlg, TRUE); /* Exit the dialog */
3294 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3295 if (!appData.highlightLastMove) {
3297 DrawPosition(FALSE, NULL);
3304 /* Pop up promotion dialog */
3306 PromotionPopup(HWND hwnd)
3310 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
3311 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
3312 hwnd, (DLGPROC)lpProc);
3313 FreeProcInstance(lpProc);
3316 /* Toggle ShowThinking */
3318 ToggleShowThinking()
3320 ShowThinkingEvent(!appData.showThinking);
3324 LoadGameDialog(HWND hwnd, char* title)
3328 char fileTitle[MSG_SIZ];
3329 f = OpenFileDialog(hwnd, FALSE, "",
3330 appData.oldSaveStyle ? "gam" : "pgn",
3332 title, &number, fileTitle, NULL);
3334 cmailMsgLoaded = FALSE;
3336 int error = GameListBuild(f);
3338 DisplayError("Cannot build game list", error);
3339 } else if (!ListEmpty(&gameList) &&
3340 ((ListGame *) gameList.tailPred)->number > 1) {
3341 GameListPopUp(f, fileTitle);
3347 LoadGame(f, number, fileTitle, FALSE);
3352 ChangedConsoleFont()
3355 CHARRANGE tmpsel, sel;
3356 MyFont *f = font[boardSize][CONSOLE_FONT];
3357 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
3358 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3361 cfmt.cbSize = sizeof(CHARFORMAT);
3362 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
3363 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
3364 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
3365 * size. This was undocumented in the version of MSVC++ that I had
3366 * when I wrote the code, but is apparently documented now.
3368 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
3369 cfmt.bCharSet = f->lf.lfCharSet;
3370 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
3371 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3372 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
3373 /* Why are the following seemingly needed too? */
3374 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3375 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
3376 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
3378 tmpsel.cpMax = -1; /*999999?*/
3379 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
3380 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
3381 /* Trying putting this here too. It still seems to tickle a RichEdit
3382 * bug: sometimes RichEdit indents the first line of a paragraph too.
3384 paraf.cbSize = sizeof(paraf);
3385 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
3386 paraf.dxStartIndent = 0;
3387 paraf.dxOffset = WRAP_INDENT;
3388 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
3389 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
3392 /*---------------------------------------------------------------------------*\
3394 * Window Proc for main window
3396 \*---------------------------------------------------------------------------*/
3398 /* Process messages for main window, etc. */
3400 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3407 char fileTitle[MSG_SIZ];
3412 case WM_PAINT: /* message: repaint portion of window */
3417 if (IsIconic(hwnd)) {
3418 /* Cheat; change the message */
3419 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
3421 return (DefWindowProc(hwnd, message, wParam, lParam));
3425 case WM_LBUTTONDOWN:
3426 case WM_MBUTTONDOWN:
3427 case WM_RBUTTONDOWN:
3433 MouseEvent(hwnd, message, wParam, lParam);
3438 if (appData.icsActive) {
3439 if (wParam == '\t') {
3440 if (GetKeyState(VK_SHIFT) < 0) {
3442 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3443 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3447 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3448 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3452 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3453 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3455 SendMessage(h, message, wParam, lParam);
3457 } else if (isalpha((char)wParam) || isdigit((char)wParam)) {
3458 PopUpMoveDialog((char)wParam);
3462 case WM_PALETTECHANGED:
3463 if (hwnd != (HWND)wParam && !appData.monoMode) {
3465 HDC hdc = GetDC(hwndMain);
3466 SelectPalette(hdc, hPal, TRUE);
3467 nnew = RealizePalette(hdc);
3469 paletteChanged = TRUE;
3473 InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/
3476 ReleaseDC(hwnd, hdc);
3480 case WM_QUERYNEWPALETTE:
3481 if (!appData.monoMode /*&& paletteChanged*/) {
3483 HDC hdc = GetDC(hwndMain);
3484 paletteChanged = FALSE;
3485 SelectPalette(hdc, hPal, FALSE);
3486 nnew = RealizePalette(hdc);
3488 InvalidateRect(hwnd, &boardRect, FALSE);
3490 ReleaseDC(hwnd, hdc);
3495 case WM_COMMAND: /* message: command from application menu */
3496 wmId = LOWORD(wParam);
3497 wmEvent = HIWORD(wParam);
3506 LoadGameDialog(hwnd, "Load Game from File");
3509 case IDM_LoadNextGame:
3513 case IDM_LoadPrevGame:
3517 case IDM_ReloadGame:
3521 case IDM_LoadPosition:
3522 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
3526 f = OpenFileDialog(hwnd, FALSE, "",
3527 appData.oldSaveStyle ? "pos" : "fen",
3529 "Load Position from File", &number, fileTitle, NULL);
3531 LoadPosition(f, number, fileTitle);
3535 case IDM_LoadNextPosition:
3539 case IDM_LoadPrevPosition:
3543 case IDM_ReloadPosition:
3548 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
3549 f = OpenFileDialog(hwnd, TRUE, defName,
3550 appData.oldSaveStyle ? "gam" : "pgn",
3552 "Save Game to File", NULL, fileTitle, NULL);
3558 case IDM_SavePosition:
3559 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
3560 f = OpenFileDialog(hwnd, TRUE, defName,
3561 appData.oldSaveStyle ? "pos" : "fen",
3563 "Save Position to File", NULL, fileTitle, NULL);
3565 SavePosition(f, 0, "");
3570 CopyGameToClipboard();