2 * WinBoard.c -- Windows NT front end to XBoard
\r
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
\r
6 * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
\r
8 * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
\r
9 * which was written and is copyrighted by Wayne Christopher.
\r
11 * The following terms apply to Digital Equipment Corporation's copyright
\r
12 * interest in XBoard:
\r
13 * ------------------------------------------------------------------------
\r
14 * All Rights Reserved
\r
16 * Permission to use, copy, modify, and distribute this software and its
\r
17 * documentation for any purpose and without fee is hereby granted,
\r
18 * provided that the above copyright notice appear in all copies and that
\r
19 * both that copyright notice and this permission notice appear in
\r
20 * supporting documentation, and that the name of Digital not be
\r
21 * used in advertising or publicity pertaining to distribution of the
\r
22 * software without specific, written prior permission.
\r
24 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
\r
25 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
\r
26 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
\r
27 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
\r
28 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
\r
29 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
\r
31 * ------------------------------------------------------------------------
\r
33 * The following terms apply to the enhanced version of XBoard distributed
\r
34 * by the Free Software Foundation:
\r
35 * ------------------------------------------------------------------------
\r
36 * This program is free software; you can redistribute it and/or modify
\r
37 * it under the terms of the GNU General Public License as published by
\r
38 * the Free Software Foundation; either version 2 of the License, or
\r
39 * (at your option) any later version.
\r
41 * This program is distributed in the hope that it will be useful,
\r
42 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
44 * GNU General Public License for more details.
\r
46 * You should have received a copy of the GNU General Public License
\r
47 * along with this program; if not, write to the Free Software
\r
48 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
\r
49 * ------------------------------------------------------------------------
\r
54 #include <windows.h>
\r
55 #include <winuser.h>
\r
56 #include <winsock.h>
\r
61 #include <sys/stat.h>
\r
64 #include <commdlg.h>
\r
66 #include <richedit.h>
\r
67 #include <mmsystem.h>
\r
75 #include "winboard.h"
\r
76 #include "frontend.h"
\r
77 #include "backend.h"
\r
79 #include "wclipbrd.h"
\r
80 #include "wgamelist.h"
\r
81 #include "wedittags.h"
\r
82 #include "woptions.h"
\r
83 #include "wsockerr.h"
\r
84 #include "defaults.h"
\r
88 POINT pos; /* window coordinates of current pos */
\r
89 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
90 POINT from; /* board coordinates of the piece's orig pos */
\r
91 POINT to; /* board coordinates of the piece's new pos */
\r
94 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
\r
97 POINT start; /* window coordinates of start pos */
\r
98 POINT pos; /* window coordinates of current pos */
\r
99 POINT lastpos; /* window coordinates of last pos - used for clipping */
\r
100 POINT from; /* board coordinates of the piece's orig pos */
\r
103 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
\r
106 POINT sq[2]; /* board coordinates of from, to squares */
\r
109 static HighlightInfo highlightInfo = { {{-1, -1}, {-1, -1}} };
\r
110 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
\r
112 /* Window class names */
\r
113 char szAppName[] = "WinBoard";
\r
114 char szConsoleName[] = "WBConsole";
\r
116 /* Title bar text */
\r
117 char szTitle[] = "WinBoard";
\r
118 char szConsoleTitle[] = "ICS Interaction";
\r
121 char *settingsFileName;
\r
122 BOOLEAN saveSettingsOnExit;
\r
123 char installDir[MSG_SIZ];
\r
125 BoardSize boardSize;
\r
126 BOOLEAN chessProgram;
\r
127 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;
\r
128 static int squareSize, lineGap;
\r
129 static int winWidth, winHeight;
\r
130 static RECT messageRect, whiteRect, blackRect;
\r
131 static char messageText[MESSAGE_TEXT_MAX];
\r
132 static int clockTimerEvent = 0;
\r
133 static int loadGameTimerEvent = 0;
\r
134 static int analysisTimerEvent = 0;
\r
135 static DelayedEventCallback delayedTimerCallback;
\r
136 static int delayedTimerEvent = 0;
\r
137 static int buttonCount = 2;
\r
138 char *icsTextMenuString;
\r
140 char *firstChessProgramNames;
\r
141 char *secondChessProgramNames;
\r
143 #define ARG_MAX 20000
\r
145 #define PALETTESIZE 256
\r
147 HINSTANCE hInst; /* current instance */
\r
148 HWND hwndMain = NULL; /* root window*/
\r
149 HWND hwndConsole = NULL;
\r
150 BOOLEAN alwaysOnTop = FALSE;
\r
152 COLORREF lightSquareColor, darkSquareColor, whitePieceColor,
\r
153 blackPieceColor, highlightSquareColor, premoveHighlightColor;
\r
155 ColorClass currentColorClass;
\r
157 HWND hCommPort = NULL; /* currently open comm port */
\r
158 static HWND hwndPause; /* pause button */
\r
159 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];
\r
160 static HBRUSH lightSquareBrush, darkSquareBrush,
\r
161 whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;
\r
162 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];
\r
163 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];
\r
164 static HPEN gridPen = NULL;
\r
165 static HPEN highlightPen = NULL;
\r
166 static HPEN premovePen = NULL;
\r
167 static NPLOGPALETTE pLogPal;
\r
168 static BOOL paletteChanged = FALSE;
\r
169 static HICON iconWhite, iconBlack, iconCurrent;
\r
170 static int doingSizing = FALSE;
\r
171 static int lastSizing = 0;
\r
172 static int prevStderrPort;
\r
174 #if __GNUC__ && !defined(_winmajor)
\r
175 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
\r
177 #define oldDialog (_winmajor < 4)
\r
180 char *defaultTextAttribs[] =
\r
182 COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,
\r
183 COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,
\r
193 int cliWidth, cliHeight;
\r
196 SizeInfo sizeInfo[] =
\r
198 { "tiny", 21, 0, 1, 1, 0, 0 },
\r
199 { "teeny", 25, 1, 1, 1, 0, 0 },
\r
200 { "dinky", 29, 1, 1, 1, 0, 0 },
\r
201 { "petite", 33, 1, 1, 1, 0, 0 },
\r
202 { "slim", 37, 2, 1, 0, 0, 0 },
\r
203 { "small", 40, 2, 1, 0, 0, 0 },
\r
204 { "mediocre", 45, 2, 1, 0, 0, 0 },
\r
205 { "middling", 49, 2, 0, 0, 0, 0 },
\r
206 { "average", 54, 2, 0, 0, 0, 0 },
\r
207 { "moderate", 58, 3, 0, 0, 0, 0 },
\r
208 { "medium", 64, 3, 0, 0, 0, 0 },
\r
209 { "bulky", 72, 3, 0, 0, 0, 0 },
\r
210 { "large", 80, 3, 0, 0, 0, 0 },
\r
211 { "big", 87, 3, 0, 0, 0, 0 },
\r
212 { "huge", 95, 3, 0, 0, 0, 0 },
\r
213 { "giant", 108, 3, 0, 0, 0, 0 },
\r
214 { "colossal", 116, 4, 0, 0, 0, 0 },
\r
215 { "titanic", 129, 4, 0, 0, 0, 0 },
\r
216 { NULL, 0, 0, 0, 0, 0, 0 }
\r
219 #define MF(x) {x, {0, }, {0, }, 0}
\r
220 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
\r
222 { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY),
\r
223 MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),
\r
224 MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },
\r
225 { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY),
\r
226 MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),
\r
227 MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },
\r
228 { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),
\r
229 MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),
\r
230 MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },
\r
231 { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),
\r
232 MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),
\r
233 MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },
\r
234 { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),
\r
235 MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),
\r
236 MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },
\r
237 { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),
\r
238 MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),
\r
239 MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },
\r
240 { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),
\r
241 MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),
\r
242 MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },
\r
243 { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),
\r
244 MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),
\r
245 MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },
\r
246 { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),
\r
247 MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),
\r
248 MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },
\r
249 { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),
\r
250 MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),
\r
251 MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },
\r
252 { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),
\r
253 MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),
\r
254 MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },
\r
255 { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),
\r
256 MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),
\r
257 MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },
\r
258 { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),
\r
259 MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),
\r
260 MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },
\r
261 { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),
\r
262 MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),
\r
263 MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },
\r
264 { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),
\r
265 MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),
\r
266 MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },
\r
267 { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),
\r
268 MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),
\r
269 MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },
\r
270 { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),
\r
271 MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),
\r
272 MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },
\r
273 { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),
\r
274 MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),
\r
275 MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },
\r
278 MyFont *font[NUM_SIZES][NUM_FONTS];
\r
287 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
\r
288 #define N_BUTTONS 5
\r
290 MyButtonDesc buttonDesc[N_BUTTONS] =
\r
292 {"<<", IDM_ToStart, NULL, NULL},
\r
293 {"<", IDM_Backward, NULL, NULL},
\r
294 {"P", IDM_Pause, NULL, NULL},
\r
295 {">", IDM_Forward, NULL, NULL},
\r
296 {">>", IDM_ToEnd, NULL, NULL},
\r
299 int tinyLayout = 0, smallLayout = 0;
\r
300 #define MENU_BAR_ITEMS 6
\r
301 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
\r
302 { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
\r
303 { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
\r
307 MySound sounds[(int)NSoundClasses];
\r
308 MyTextAttribs textAttribs[(int)NColorClasses];
\r
310 MyColorizeAttribs colorizeAttribs[] = {
\r
311 { (COLORREF)0, 0, "Shout Text" },
\r
312 { (COLORREF)0, 0, "SShout/CShout" },
\r
313 { (COLORREF)0, 0, "Channel 1 Text" },
\r
314 { (COLORREF)0, 0, "Channel Text" },
\r
315 { (COLORREF)0, 0, "Kibitz Text" },
\r
316 { (COLORREF)0, 0, "Tell Text" },
\r
317 { (COLORREF)0, 0, "Challenge Text" },
\r
318 { (COLORREF)0, 0, "Request Text" },
\r
319 { (COLORREF)0, 0, "Seek Text" },
\r
320 { (COLORREF)0, 0, "Normal Text" },
\r
321 { (COLORREF)0, 0, "None" }
\r
326 static char *commentTitle;
\r
327 static char *commentText;
\r
328 static int commentIndex;
\r
329 static Boolean editComment = FALSE;
\r
330 HWND commentDialog = NULL;
\r
331 BOOLEAN commentDialogUp = FALSE;
\r
332 static int commentX, commentY, commentH, commentW;
\r
334 static char *analysisTitle;
\r
335 static char *analysisText;
\r
336 HWND analysisDialog = NULL;
\r
337 BOOLEAN analysisDialogUp = FALSE;
\r
338 static int analysisX, analysisY, analysisH, analysisW;
\r
340 char errorMessage[2*MSG_SIZ];
\r
341 HWND errorDialog = NULL;
\r
342 BOOLEAN moveErrorMessageUp = FALSE;
\r
343 BOOLEAN consoleEcho = TRUE;
\r
344 CHARFORMAT consoleCF;
\r
345 COLORREF consoleBackgroundColor;
\r
347 char *programVersion;
\r
353 typedef int CPKind;
\r
362 SOCKET sock2; /* stderr socket for OpenRcmd */
\r
365 #define INPUT_SOURCE_BUF_SIZE 4096
\r
367 typedef struct _InputSource {
\r
374 char buf[INPUT_SOURCE_BUF_SIZE];
\r
378 InputCallback func;
\r
379 struct _InputSource *second; /* for stderr thread on CPRcmd */
\r
383 InputSource *consoleInputSource;
\r
388 VOID ConsoleOutput(char* data, int length, int forceVisible);
\r
389 VOID ConsoleCreate();
\r
391 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
392 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
\r
393 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
\r
394 VOID ParseCommSettings(char *arg, DCB *dcb);
\r
396 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
\r
397 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
\r
398 void ParseIcsTextMenu(char *icsTextMenuString);
\r
399 VOID PopUpMoveDialog(char firstchar);
\r
400 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
\r
403 * Setting "frozen" should disable all user input other than deleting
\r
404 * the window. We do this while engines are initializing themselves.
\r
406 static int frozen = 0;
\r
407 static int oldMenuItemState[MENU_BAR_ITEMS];
\r
413 if (frozen) return;
\r
415 hmenu = GetMenu(hwndMain);
\r
416 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
417 oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
\r
419 DrawMenuBar(hwndMain);
\r
422 /* Undo a FreezeUI */
\r
428 if (!frozen) return;
\r
430 hmenu = GetMenu(hwndMain);
\r
431 for (i=0; i<MENU_BAR_ITEMS; i++) {
\r
432 EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
\r
434 DrawMenuBar(hwndMain);
\r
437 /*---------------------------------------------------------------------------*\
\r
441 \*---------------------------------------------------------------------------*/
\r
444 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
\r
445 LPSTR lpCmdLine, int nCmdShow)
\r
448 HANDLE hAccelMain, hAccelNoAlt;
\r
452 LoadLibrary("RICHED32.DLL");
\r
453 consoleCF.cbSize = sizeof(CHARFORMAT);
\r
455 if (!InitApplication(hInstance)) {
\r
458 if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
\r
462 hAccelMain = LoadAccelerators (hInstance, szAppName);
\r
463 hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
\r
465 /* Acquire and dispatch messages until a WM_QUIT message is received. */
\r
467 while (GetMessage(&msg, /* message structure */
\r
468 NULL, /* handle of window receiving the message */
\r
469 0, /* lowest message to examine */
\r
470 0)) /* highest message to examine */
\r
472 if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
\r
473 !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
\r
474 !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
\r
475 !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
\r
476 !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&
\r
477 !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
\r
478 TranslateMessage(&msg); /* Translates virtual key codes */
\r
479 DispatchMessage(&msg); /* Dispatches message to window */
\r
484 return (msg.wParam); /* Returns the value from PostQuitMessage */
\r
487 /*---------------------------------------------------------------------------*\
\r
489 * Initialization functions
\r
491 \*---------------------------------------------------------------------------*/
\r
494 InitApplication(HINSTANCE hInstance)
\r
498 /* Fill in window class structure with parameters that describe the */
\r
501 wc.style = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
\r
502 wc.lpfnWndProc = (WNDPROC)WndProc; /* Window Procedure */
\r
503 wc.cbClsExtra = 0; /* No per-class extra data. */
\r
504 wc.cbWndExtra = 0; /* No per-window extra data. */
\r
505 wc.hInstance = hInstance; /* Owner of this class */
\r
506 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
507 wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* Cursor */
\r
508 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
\r
509 wc.lpszMenuName = szAppName; /* Menu name from .RC */
\r
510 wc.lpszClassName = szAppName; /* Name to register as */
\r
512 /* Register the window class and return success/failure code. */
\r
513 if (!RegisterClass(&wc)) return FALSE;
\r
515 wc.style = CS_HREDRAW | CS_VREDRAW;
\r
516 wc.lpfnWndProc = (WNDPROC)ConsoleWndProc;
\r
518 wc.cbWndExtra = DLGWINDOWEXTRA;
\r
519 wc.hInstance = hInstance;
\r
520 wc.hIcon = LoadIcon(hInstance, "icon_white");
\r
521 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
\r
522 wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
\r
523 wc.lpszMenuName = NULL;
\r
524 wc.lpszClassName = szConsoleName;
\r
526 if (!RegisterClass(&wc)) return FALSE;
\r
531 /* Set by InitInstance, used by EnsureOnScreen */
\r
532 int screenHeight, screenWidth;
\r
535 EnsureOnScreen(int *x, int *y)
\r
537 /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
\r
538 if (*x > screenWidth - 32) *x = 0;
\r
539 if (*y > screenHeight - 32) *y = 0;
\r
543 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
\r
545 HWND hwnd; /* Main window handle. */
\r
547 WINDOWPLACEMENT wp;
\r
550 hInst = hInstance; /* Store instance handle in our global variable */
\r
552 if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
\r
553 *filepart = NULLCHAR;
\r
555 GetCurrentDirectory(MSG_SIZ, installDir);
\r
557 InitAppData(lpCmdLine); /* Get run-time parameters */
\r
558 if (appData.debugMode) {
\r
559 debugFP = fopen("winboard.debug", "w");
\r
560 setbuf(debugFP, NULL);
\r
565 /* Create a main window for this application instance. */
\r
566 hwnd = CreateWindow(szAppName, szTitle,
\r
567 (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
\r
568 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
\r
569 NULL, NULL, hInstance, NULL);
\r
572 /* If window could not be created, return "failure" */
\r
577 iconWhite = LoadIcon(hInstance, "icon_white");
\r
578 iconBlack = LoadIcon(hInstance, "icon_black");
\r
579 iconCurrent = iconWhite;
\r
580 InitDrawingColors();
\r
581 screenHeight = GetSystemMetrics(SM_CYSCREEN);
\r
582 screenWidth = GetSystemMetrics(SM_CXSCREEN);
\r
583 for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
\r
584 /* Compute window size for each board size, and use the largest
\r
585 size that fits on this screen as the default. */
\r
586 InitDrawingSizes((BoardSize)ibs, 0);
\r
587 if (boardSize == (BoardSize)-1 &&
\r
588 winHeight <= screenHeight && winWidth <= screenWidth) {
\r
589 boardSize = (BoardSize)ibs;
\r
592 InitDrawingSizes(boardSize, 0);
\r
594 buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
\r
596 /* Make a console window if needed */
\r
597 if (appData.icsActive) {
\r
603 /* Make the window visible; update its client area; and return "success" */
\r
604 EnsureOnScreen(&boardX, &boardY);
\r
605 wp.length = sizeof(WINDOWPLACEMENT);
\r
607 wp.showCmd = nCmdShow;
\r
608 wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
\r
609 wp.rcNormalPosition.left = boardX;
\r
610 wp.rcNormalPosition.right = boardX + winWidth;
\r
611 wp.rcNormalPosition.top = boardY;
\r
612 wp.rcNormalPosition.bottom = boardY + winHeight;
\r
613 SetWindowPlacement(hwndMain, &wp);
\r
615 SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
616 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
619 SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
\r
620 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
\r
622 ShowWindow(hwndConsole, nCmdShow);
\r
624 UpdateWindow(hwnd);
\r
632 ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone,
\r
633 ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,
\r
634 ArgSettingsFilename
\r
642 String *pString; // ArgString
\r
643 int *pInt; // ArgInt
\r
644 float *pFloat; // ArgFloat
\r
645 Boolean *pBoolean; // ArgBoolean
\r
646 COLORREF *pColor; // ArgColor
\r
647 ColorClass cc; // ArgAttribs
\r
648 String *pFilename; // ArgFilename
\r
649 BoardSize *pBoardSize; // ArgBoardSize
\r
650 int whichFont; // ArgFont
\r
651 DCB *pDCB; // ArgCommSettings
\r
652 String *pFilename; // ArgSettingsFilename
\r
660 ArgDescriptor argDescriptors[] = {
\r
661 /* positional arguments */
\r
662 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
\r
663 { "", ArgNone, NULL },
\r
664 /* keyword arguments */
\r
665 { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },
\r
666 { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },
\r
667 { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },
\r
668 { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },
\r
669 { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },
\r
670 { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },
\r
671 { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },
\r
672 { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },
\r
673 { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },
\r
674 { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },
\r
675 { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },
\r
676 { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },
\r
677 { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },
\r
678 { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },
\r
679 { "initString", ArgString, (LPVOID) &appData.initString, FALSE },
\r
680 { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },
\r
681 { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },
\r
682 { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,
\r
684 { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,
\r
686 { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,
\r
688 { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },
\r
689 { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,
\r
691 { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },
\r
692 { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },
\r
693 { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },
\r
694 { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
\r
695 { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
\r
696 { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },
\r
697 { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },
\r
698 { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
\r
699 { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
\r
700 { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },
\r
701 { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },
\r
702 { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },
\r
703 { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },
\r
704 { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
\r
705 { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
\r
706 { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
\r
707 { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
\r
708 /*!!bitmapDirectory?*/
\r
709 { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
\r
710 { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
\r
711 { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
\r
712 { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
\r
713 { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },
\r
714 { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },
\r
715 { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },
\r
716 { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },
\r
717 { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },
\r
718 { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },
\r
719 { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },
\r
720 { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },
\r
721 { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
\r
722 { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
\r
723 { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },
\r
724 { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },
\r
725 { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },
\r
726 { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },
\r
727 { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
\r
728 { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
\r
729 { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
\r
730 { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
\r
731 { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
\r
732 { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
\r
733 { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },
\r
734 { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },
\r
735 { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
\r
736 { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
\r
737 { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },
\r
738 { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },
\r
739 { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },
\r
740 { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
\r
741 { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
\r
742 { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
\r
743 { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
\r
744 { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },
\r
745 { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },
\r
746 { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },
\r
747 { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },
\r
748 { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
\r
749 { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
\r
750 { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
\r
751 { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
\r
752 { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
\r
753 { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
\r
754 { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
\r
755 { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
\r
756 { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },
\r
757 { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },
\r
758 { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
\r
759 { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
\r
760 { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },
\r
761 { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },
\r
762 { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },
\r
763 { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },
\r
764 { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
\r
765 { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
\r
766 { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },
\r
767 { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },
\r
768 { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
\r
769 { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
\r
770 { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },
\r
771 { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },
\r
772 { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
\r
773 { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
\r
774 { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },
\r
775 { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },
\r
776 { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
\r
777 { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
\r
778 { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },
\r
779 { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },
\r
780 { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
\r
781 { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
\r
782 { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },
\r
783 { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },
\r
784 { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
\r
785 { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
\r
786 { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },
\r
787 { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },
\r
788 { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
\r
789 { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
\r
790 { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },
\r
791 { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },
\r
792 { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
\r
793 { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
\r
794 { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },
\r
795 { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },
\r
796 { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
\r
797 { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
\r
798 { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },
\r
799 { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },
\r
800 { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
\r
801 { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
\r
802 { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors,
\r
803 FALSE }, /* only so that old WinBoard.ini files from betas can be read */
\r
804 { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },
\r
805 { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },
\r
806 { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },
\r
807 { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },
\r
808 { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },
\r
809 { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },
\r
810 { "boardSize", ArgBoardSize, (LPVOID) &boardSize,
\r
811 TRUE }, /* must come after all fonts */
\r
812 { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },
\r
813 { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,
\r
814 FALSE }, /* historical; kept only so old winboard.ini files will parse */
\r
815 { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },
\r
816 { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },
\r
817 { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
\r
818 { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
\r
819 { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },
\r
820 { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },
\r
821 { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
\r
822 { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
\r
823 { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },
\r
824 { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },
\r
825 { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
\r
826 { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
\r
827 { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },
\r
828 { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },
\r
829 { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
\r
830 { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
\r
831 { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },
\r
832 { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },
\r
833 { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
\r
834 { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
\r
835 { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },
\r
836 { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },
\r
837 { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
\r
838 { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
\r
839 { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },
\r
840 { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },
\r
841 { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
\r
842 { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
\r
844 { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
\r
845 { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
\r
847 { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },
\r
848 { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
\r
849 { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
\r
850 { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
\r
851 { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },
\r
852 { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },
\r
853 { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
\r
854 { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
\r
855 { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },
\r
856 { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },
\r
857 { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
\r
858 { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
\r
859 { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },
\r
860 { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },
\r
861 { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
\r
862 { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
\r
863 { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },
\r
864 { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },
\r
865 { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
\r
866 { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
\r
867 { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },
\r
868 { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },
\r
869 { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },
\r
870 { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },
\r
871 { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },
\r
872 { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },
\r
873 { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
\r
874 { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
\r
875 { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },
\r
876 { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },
\r
877 { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },
\r
878 { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
\r
879 { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
\r
880 { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },
\r
881 { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},
\r
882 { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},
\r
883 { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
\r
884 { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
\r
885 { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},
\r
886 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
\r
887 { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
\r
888 { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },
\r
889 { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
\r
890 { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
\r
891 { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },
\r
892 { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },
\r
893 { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },
\r
894 { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },
\r
895 { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },
\r
896 { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },
\r
897 { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },
\r
898 { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
\r
899 { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
\r
900 { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },
\r
901 { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },
\r
902 { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
\r
903 { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
\r
904 { "highlightLastMove", ArgBoolean,
\r
905 (LPVOID) &appData.highlightLastMove, TRUE },
\r
906 { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },
\r
907 { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
\r
908 { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
\r
909 { "highlightDragging", ArgBoolean,
\r
910 (LPVOID) &appData.highlightDragging, TRUE },
\r
911 { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },
\r
912 { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
\r
913 { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
\r
914 { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },
\r
915 { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },
\r
916 { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
\r
917 { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
\r
918 { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },
\r
919 { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },
\r
920 { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },
\r
921 { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },
\r
922 { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },
\r
923 { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },
\r
924 { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },
\r
925 { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },
\r
926 { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },
\r
927 { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },
\r
928 { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },
\r
929 { "soundShout", ArgFilename,
\r
930 (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },
\r
931 { "soundSShout", ArgFilename,
\r
932 (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },
\r
933 { "soundChannel1", ArgFilename,
\r
934 (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },
\r
935 { "soundChannel", ArgFilename,
\r
936 (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },
\r
937 { "soundKibitz", ArgFilename,
\r
938 (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },
\r
939 { "soundTell", ArgFilename,
\r
940 (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },
\r
941 { "soundChallenge", ArgFilename,
\r
942 (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },
\r
943 { "soundRequest", ArgFilename,
\r
944 (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },
\r
945 { "soundSeek", ArgFilename,
\r
946 (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },
\r
947 { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },
\r
948 { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },
\r
949 { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },
\r
950 { "soundIcsLoss", ArgFilename,
\r
951 (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },
\r
952 { "soundIcsDraw", ArgFilename,
\r
953 (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },
\r
954 { "soundIcsUnfinished", ArgFilename,
\r
955 (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},
\r
956 { "soundIcsAlarm", ArgFilename,
\r
957 (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },
\r
958 { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },
\r
959 { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },
\r
960 { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
\r
961 { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
\r
962 { "reuseChessPrograms", ArgBoolean,
\r
963 (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */
\r
964 { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },
\r
965 { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },
\r
966 { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
\r
967 { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
\r
968 { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },
\r
969 { "x", ArgInt, (LPVOID) &boardX, TRUE },
\r
970 { "y", ArgInt, (LPVOID) &boardY, TRUE },
\r
971 { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },
\r
972 { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },
\r
973 { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },
\r
974 { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },
\r
975 { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },
\r
976 { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },
\r
977 { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },
\r
978 { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },
\r
979 { "commentX", ArgInt, (LPVOID) &commentX, TRUE },
\r
980 { "commentY", ArgInt, (LPVOID) &commentY, TRUE },
\r
981 { "commentW", ArgInt, (LPVOID) &commentW, TRUE },
\r
982 { "commentH", ArgInt, (LPVOID) &commentH, TRUE },
\r
983 { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },
\r
984 { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },
\r
985 { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },
\r
986 { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },
\r
987 { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },
\r
988 { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },
\r
989 { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },
\r
990 { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },
\r
991 { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
\r
992 { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
\r
993 { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },
\r
994 { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },
\r
995 { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },
\r
996 { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },
\r
997 { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },
\r
998 { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },
\r
999 { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },
\r
1000 { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,
\r
1002 { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,
\r
1004 { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },
\r
1005 { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },
\r
1006 { "variant", ArgString, (LPVOID) &appData.variant, FALSE },
\r
1007 { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,
\r
1009 { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,
\r
1011 { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },
\r
1012 { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },
\r
1013 { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
\r
1014 { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
\r
1016 { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },
\r
1017 { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },
\r
1018 { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
\r
1019 { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
\r
1020 { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },
\r
1021 { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },
\r
1022 { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
\r
1023 { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
\r
1024 { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },
\r
1025 { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },
\r
1026 { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },
\r
1027 { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },
\r
1028 { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,
\r
1030 { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },
\r
1031 { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },
\r
1032 { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },
\r
1033 { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
\r
1034 { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
\r
1035 { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },
\r
1036 { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,
\r
1038 { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
\r
1039 { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
\r
1040 { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
\r
1041 { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },
\r
1042 { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },
\r
1043 { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },
\r
1044 { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },
\r
1045 { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
\r
1046 { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
\r
1047 { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },
\r
1048 { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },
\r
1049 { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
\r
1050 { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
\r
1051 { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },
\r
1052 { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },
\r
1053 { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },
\r
1054 /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */
\r
1055 { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },
\r
1057 { NULL, ArgNone, NULL, FALSE }
\r
1061 /* Kludge for indirection files on command line */
\r
1062 char* lastIndirectionFilename;
\r
1063 ArgDescriptor argDescriptorIndirection =
\r
1064 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };
\r
1068 ExitArgError(char *msg, char *badArg)
\r
1070 char buf[MSG_SIZ];
\r
1072 sprintf(buf, "%s %s", msg, badArg);
\r
1073 DisplayFatalError(buf, 0, 2);
\r
1077 /* Command line font name parser. NULL name means do nothing.
\r
1078 Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
\r
1079 For backward compatibility, syntax without the colon is also
\r
1080 accepted, but font names with digits in them won't work in that case.
\r
1083 ParseFontName(char *name, MyFontParams *mfp)
\r
1086 if (name == NULL) return;
\r
1088 q = strchr(p, ':');
\r
1090 if (q - p >= sizeof(mfp->faceName))
\r
1091 ExitArgError("Font name too long:", name);
\r
1092 memcpy(mfp->faceName, p, q - p);
\r
1093 mfp->faceName[q - p] = NULLCHAR;
\r
1096 q = mfp->faceName;
\r
1097 while (*p && !isdigit(*p)) {
\r
1099 if (q - mfp->faceName >= sizeof(mfp->faceName))
\r
1100 ExitArgError("Font name too long:", name);
\r
1102 while (q > mfp->faceName && q[-1] == ' ') q--;
\r
1105 if (!*p) ExitArgError("Font point size missing:", name);
\r
1106 mfp->pointSize = (float) atof(p);
\r
1107 mfp->bold = (strchr(p, 'b') != NULL);
\r
1108 mfp->italic = (strchr(p, 'i') != NULL);
\r
1109 mfp->underline = (strchr(p, 'u') != NULL);
\r
1110 mfp->strikeout = (strchr(p, 's') != NULL);
\r
1113 /* Color name parser.
\r
1114 X version accepts X color names, but this one
\r
1115 handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
\r
1117 ParseColorName(char *name)
\r
1119 int red, green, blue, count;
\r
1120 char buf[MSG_SIZ];
\r
1122 count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
\r
1124 count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d",
\r
1125 &red, &green, &blue);
\r
1128 sprintf(buf, "Can't parse color name %s", name);
\r
1129 DisplayError(buf, 0);
\r
1130 return RGB(0, 0, 0);
\r
1132 return PALETTERGB(red, green, blue);
\r
1136 void ParseAttribs(COLORREF *color, int *effects, char* argValue)
\r
1138 char *e = argValue;
\r
1142 if (*e == 'b') eff |= CFE_BOLD;
\r
1143 else if (*e == 'i') eff |= CFE_ITALIC;
\r
1144 else if (*e == 'u') eff |= CFE_UNDERLINE;
\r
1145 else if (*e == 's') eff |= CFE_STRIKEOUT;
\r
1146 else if (*e == '#' || isdigit(*e)) break;
\r
1150 *color = ParseColorName(e);
\r
1155 ParseBoardSize(char *name)
\r
1157 BoardSize bs = SizeTiny;
\r
1158 while (sizeInfo[bs].name != NULL) {
\r
1159 if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;
\r
1162 ExitArgError("Unrecognized board size value", name);
\r
1163 return bs; /* not reached */
\r
1168 StringGet(void *getClosure)
\r
1170 char **p = (char **) getClosure;
\r
1175 FileGet(void *getClosure)
\r
1178 FILE* f = (FILE*) getClosure;
\r
1187 /* Parse settings file named "name". If file found, return the
\r
1188 full name in fullname and return TRUE; else return FALSE */
\r
1190 ParseSettingsFile(char *name, char fullname[MSG_SIZ])
\r
1195 if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {
\r
1196 f = fopen(fullname, "r");
\r
1198 ParseArgs(FileGet, f);
\r
1207 ParseArgs(GetFunc get, void *cl)
\r
1209 char argName[ARG_MAX];
\r
1210 char argValue[ARG_MAX];
\r
1211 ArgDescriptor *ad;
\r
1220 while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);
\r
1221 if (ch == NULLCHAR) break;
\r
1223 /* Comment to end of line */
\r
1225 while (ch != '\n' && ch != NULLCHAR) ch = get(cl);
\r
1227 } else if (ch == '/' || ch == '-') {
\r
1230 while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&
\r
1231 ch != '\n' && ch != '\t') {
\r
1237 for (ad = argDescriptors; ad->argName != NULL; ad++)
\r
1238 if (strcmp(ad->argName, argName + 1) == 0) break;
\r
1240 if (ad->argName == NULL)
\r
1241 ExitArgError("Unrecognized argument", argName);
\r
1243 } else if (ch == '@') {
\r
1244 /* Indirection file */
\r
1245 ad = &argDescriptorIndirection;
\r
1248 /* Positional argument */
\r
1249 ad = &argDescriptors[posarg++];
\r
1250 strcpy(argName, ad->argName);
\r
1253 if (ad->argType == ArgTrue) {
\r
1254 *(Boolean *) ad->argLoc = TRUE;
\r
1257 if (ad->argType == ArgFalse) {
\r
1258 *(Boolean *) ad->argLoc = FALSE;
\r
1262 while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);
\r
1263 if (ch == NULLCHAR || ch == '\n') {
\r
1264 ExitArgError("No value provided for argument", argName);
\r
1268 // Quoting with { }. No characters have to (or can) be escaped.
\r
1269 // Thus the string cannot contain a '}' character.
\r
1289 } else if (ch == '\'' || ch == '"') {
\r
1290 // Quoting with ' ' or " ", with \ as escape character.
\r
1291 // Inconvenient for long strings that may contain Windows filenames.
\r
1308 if (ch == start) {
\r
1317 if (ad->argType == ArgFilename
\r
1318 || ad->argType == ArgSettingsFilename) {
\r
1324 ExitArgError("Incomplete \\ escape in value for", argName);
\r
1348 for (i = 0; i < 3; i++) {
\r
1349 if (ch >= '0' && ch <= '7') {
\r
1350 octval = octval*8 + (ch - '0');
\r
1357 *q++ = (char) octval;
\r
1368 while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {
\r
1375 switch (ad->argType) {
\r
1377 *(int *) ad->argLoc = atoi(argValue);
\r
1381 *(float *) ad->argLoc = (float) atof(argValue);
\r
1386 *(char **) ad->argLoc = strdup(argValue);
\r
1389 case ArgSettingsFilename:
\r
1391 char fullname[MSG_SIZ];
\r
1392 if (ParseSettingsFile(argValue, fullname)) {
\r
1393 if (ad->argLoc != NULL) {
\r
1394 *(char **) ad->argLoc = strdup(fullname);
\r
1397 if (ad->argLoc != NULL) {
\r
1399 ExitArgError("Failed to open indirection file", argValue);
\r
1406 switch (argValue[0]) {
\r
1409 *(Boolean *) ad->argLoc = TRUE;
\r
1413 *(Boolean *) ad->argLoc = FALSE;
\r
1416 ExitArgError("Unrecognized boolean argument value", argValue);
\r
1422 *(COLORREF *)ad->argLoc = ParseColorName(argValue);
\r
1425 case ArgAttribs: {
\r
1426 ColorClass cc = (ColorClass)ad->argLoc;
\r
1427 ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);
\r
1431 case ArgBoardSize:
\r
1432 *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);
\r
1436 ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);
\r
1439 case ArgCommSettings:
\r
1440 ParseCommSettings(argValue, &dcb);
\r
1444 ExitArgError("Unrecognized argument", argValue);
\r
1451 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
\r
1453 HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
\r
1454 lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
\r
1457 lf->lfEscapement = 0;
\r
1458 lf->lfOrientation = 0;
\r
1459 lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
\r
1460 lf->lfItalic = mfp->italic;
\r
1461 lf->lfUnderline = mfp->underline;
\r
1462 lf->lfStrikeOut = mfp->strikeout;
\r
1463 lf->lfCharSet = DEFAULT_CHARSET;
\r
1464 lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
\r
1465 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
\r
1466 lf->lfQuality = DEFAULT_QUALITY;
\r
1467 lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
\r
1468 strcpy(lf->lfFaceName, mfp->faceName);
\r
1472 CreateFontInMF(MyFont *mf)
\r
1474 LFfromMFP(&mf->lf, &mf->mfp);
\r
1475 if (mf->hf) DeleteObject(mf->hf);
\r
1476 mf->hf = CreateFontIndirect(&mf->lf);
\r
1480 SetDefaultTextAttribs()
\r
1483 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
\r
1484 ParseAttribs(&textAttribs[cc].color,
\r
1485 &textAttribs[cc].effects,
\r
1486 defaultTextAttribs[cc]);
\r
1491 SetDefaultSounds()
\r
1495 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
\r
1496 textAttribs[cc].sound.name = strdup("");
\r
1497 textAttribs[cc].sound.data = NULL;
\r
1499 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1500 sounds[sc].name = strdup("");
\r
1501 sounds[sc].data = NULL;
\r
1503 sounds[(int)SoundBell].name = strdup(SOUND_BELL);
\r
1511 for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
\r
1512 MyLoadSound(&textAttribs[cc].sound);
\r
1514 for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
\r
1515 MyLoadSound(&sounds[sc]);
\r
1520 InitAppData(LPSTR lpCmdLine)
\r
1523 char buf[ARG_MAX], currDir[MSG_SIZ];
\r
1526 programName = szAppName;
\r
1528 /* Initialize to defaults */
\r
1529 lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);
\r
1530 darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);
\r
1531 whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);
\r
1532 blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);
\r
1533 highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);
\r
1534 premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);
\r
1535 consoleBackgroundColor = ParseColorName(COLOR_BKGD);
\r
1536 SetDefaultTextAttribs();
\r
1537 SetDefaultSounds();
\r
1538 appData.movesPerSession = MOVES_PER_SESSION;
\r
1539 appData.initString = INIT_STRING;
\r
1540 appData.secondInitString = INIT_STRING;
\r
1541 appData.firstComputerString = COMPUTER_STRING;
\r
1542 appData.secondComputerString = COMPUTER_STRING;
\r
1543 appData.firstChessProgram = FIRST_CHESS_PROGRAM;
\r
1544 appData.secondChessProgram = SECOND_CHESS_PROGRAM;
\r
1545 appData.firstPlaysBlack = FALSE;
\r
1546 appData.noChessProgram = FALSE;
\r
1547 chessProgram = FALSE;
\r
1548 appData.firstHost = FIRST_HOST;
\r
1549 appData.secondHost = SECOND_HOST;
\r
1550 appData.firstDirectory = FIRST_DIRECTORY;
\r
1551 appData.secondDirectory = SECOND_DIRECTORY;
\r
1552 appData.bitmapDirectory = "";
\r
1553 appData.remoteShell = REMOTE_SHELL;
\r
1554 appData.remoteUser = "";
\r
1555 appData.timeDelay = TIME_DELAY;
\r
1556 appData.timeControl = TIME_CONTROL;
\r
1557 appData.timeIncrement = TIME_INCREMENT;
\r
1558 appData.icsActive = FALSE;
\r
1559 appData.icsHost = "";
\r
1560 appData.icsPort = ICS_PORT;
\r
1561 appData.icsCommPort = ICS_COMM_PORT;
\r
1562 appData.icsLogon = ICS_LOGON;
\r
1563 appData.icsHelper = "";
\r
1564 appData.useTelnet = FALSE;
\r
1565 appData.telnetProgram = TELNET_PROGRAM;
\r
1566 appData.gateway = "";
\r
1567 appData.loadGameFile = "";
\r
1568 appData.loadGameIndex = 0;
\r
1569 appData.saveGameFile = "";
\r
1570 appData.autoSaveGames = FALSE;
\r
1571 appData.loadPositionFile = "";
\r
1572 appData.loadPositionIndex = 1;
\r
1573 appData.savePositionFile = "";
\r
1574 appData.matchMode = FALSE;
\r
1575 appData.matchGames = 0;
\r
1576 appData.monoMode = FALSE;
\r
1577 appData.debugMode = FALSE;
\r
1578 appData.clockMode = TRUE;
\r
1579 boardSize = (BoardSize) -1; /* determine by screen size */
\r
1580 appData.Iconic = FALSE; /*unused*/
\r
1581 appData.searchTime = "";
\r
1582 appData.searchDepth = 0;
\r
1583 appData.showCoords = FALSE;
\r
1584 appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/
\r
1585 appData.autoCallFlag = FALSE;
\r
1586 appData.flipView = FALSE;
\r
1587 appData.autoFlipView = TRUE;
\r
1588 appData.cmailGameName = "";
\r
1589 appData.alwaysPromoteToQueen = FALSE;
\r
1590 appData.oldSaveStyle = FALSE;
\r
1591 appData.quietPlay = FALSE;
\r
1592 appData.showThinking = FALSE;
\r
1593 appData.ponderNextMove = TRUE;
\r
1594 appData.periodicUpdates = TRUE;
\r
1595 appData.popupExitMessage = TRUE;
\r
1596 appData.popupMoveErrors = FALSE;
\r
1597 appData.autoObserve = FALSE;
\r
1598 appData.autoComment = FALSE;
\r
1599 appData.animate = TRUE;
\r
1600 appData.animSpeed = 10;
\r
1601 appData.animateDragging = TRUE;
\r
1602 appData.highlightLastMove = TRUE;
\r
1603 appData.getMoveList = TRUE;
\r
1604 appData.testLegality = TRUE;
\r
1605 appData.premove = TRUE;
\r
1606 appData.premoveWhite = FALSE;
\r
1607 appData.premoveWhiteText = "";
\r
1608 appData.premoveBlack = FALSE;
\r
1609 appData.premoveBlackText = "";
\r
1610 appData.icsAlarm = TRUE;
\r
1611 appData.icsAlarmTime = 5000;
\r
1612 appData.autoRaiseBoard = TRUE;
\r
1613 appData.localLineEditing = TRUE;
\r
1614 appData.colorize = TRUE;
\r
1615 appData.reuseFirst = TRUE;
\r
1616 appData.reuseSecond = TRUE;
\r
1617 appData.blindfold = FALSE;
\r
1618 dcb.DCBlength = sizeof(DCB);
\r
1619 dcb.BaudRate = 9600;
\r
1620 dcb.fBinary = TRUE;
\r
1621 dcb.fParity = FALSE;
\r
1622 dcb.fOutxCtsFlow = FALSE;
\r
1623 dcb.fOutxDsrFlow = FALSE;
\r
1624 dcb.fDtrControl = DTR_CONTROL_ENABLE;
\r
1625 dcb.fDsrSensitivity = FALSE;
\r
1626 dcb.fTXContinueOnXoff = TRUE;
\r
1627 dcb.fOutX = FALSE;
\r
1629 dcb.fNull = FALSE;
\r
1630 dcb.fRtsControl = RTS_CONTROL_ENABLE;
\r
1631 dcb.fAbortOnError = FALSE;
\r
1632 dcb.wReserved = 0;
\r
1634 dcb.Parity = SPACEPARITY;
\r
1635 dcb.StopBits = ONESTOPBIT;
\r
1636 settingsFileName = SETTINGS_FILE;
\r
1637 saveSettingsOnExit = TRUE;
\r
1638 boardX = CW_USEDEFAULT;
\r
1639 boardY = CW_USEDEFAULT;
\r
1640 consoleX = CW_USEDEFAULT;
\r
1641 consoleY = CW_USEDEFAULT;
\r
1642 consoleW = CW_USEDEFAULT;
\r
1643 consoleH = CW_USEDEFAULT;
\r
1644 analysisX = CW_USEDEFAULT;
\r
1645 analysisY = CW_USEDEFAULT;
\r
1646 analysisW = CW_USEDEFAULT;
\r
1647 analysisH = CW_USEDEFAULT;
\r
1648 commentX = CW_USEDEFAULT;
\r
1649 commentY = CW_USEDEFAULT;
\r
1650 commentW = CW_USEDEFAULT;
\r
1651 commentH = CW_USEDEFAULT;
\r
1652 editTagsX = CW_USEDEFAULT;
\r
1653 editTagsY = CW_USEDEFAULT;
\r
1654 editTagsW = CW_USEDEFAULT;
\r
1655 editTagsH = CW_USEDEFAULT;
\r
1656 gameListX = CW_USEDEFAULT;
\r
1657 gameListY = CW_USEDEFAULT;
\r
1658 gameListW = CW_USEDEFAULT;
\r
1659 gameListH = CW_USEDEFAULT;
\r
1660 icsTextMenuString = ICS_TEXT_MENU_DEFAULT;
\r
1661 icsNames = ICS_NAMES;
\r
1662 firstChessProgramNames = FCP_NAMES;
\r
1663 secondChessProgramNames = SCP_NAMES;
\r
1664 appData.initialMode = "";
\r
1665 appData.variant = "normal";
\r
1666 appData.firstProtocolVersion = PROTOVER;
\r
1667 appData.secondProtocolVersion = PROTOVER;
\r
1668 appData.showButtonBar = TRUE;
\r
1670 appData.zippyTalk = ZIPPY_TALK;
\r
1671 appData.zippyPlay = ZIPPY_PLAY;
\r
1672 appData.zippyLines = ZIPPY_LINES;
\r
1673 appData.zippyPinhead = ZIPPY_PINHEAD;
\r
1674 appData.zippyPassword = ZIPPY_PASSWORD;
\r
1675 appData.zippyPassword2 = ZIPPY_PASSWORD2;
\r
1676 appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;
\r
1677 appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;
\r
1678 appData.zippyUseI = ZIPPY_USE_I;
\r
1679 appData.zippyBughouse = ZIPPY_BUGHOUSE;
\r
1680 appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;
\r
1681 appData.zippyGameEnd = ZIPPY_GAME_END;
\r
1682 appData.zippyGameStart = ZIPPY_GAME_START;
\r
1683 appData.zippyAdjourn = ZIPPY_ADJOURN;
\r
1684 appData.zippyAbort = ZIPPY_ABORT;
\r
1685 appData.zippyVariants = ZIPPY_VARIANTS;
\r
1686 appData.zippyMaxGames = ZIPPY_MAX_GAMES;
\r
1687 appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;
\r
1690 /* Point font array elements to structures and
\r
1691 parse default font names */
\r
1692 for (i=0; i<NUM_FONTS; i++) {
\r
1693 for (j=0; j<NUM_SIZES; j++) {
\r
1694 font[j][i] = &fontRec[j][i];
\r
1695 ParseFontName(font[j][i]->def, &font[j][i]->mfp);
\r
1699 /* Parse default settings file if any */
\r
1700 if (ParseSettingsFile(settingsFileName, buf)) {
\r
1701 settingsFileName = strdup(buf);
\r
1704 /* Parse command line */
\r
1705 ParseArgs(StringGet, &lpCmdLine);
\r
1707 /* Propagate options that affect others */
\r
1708 if (appData.matchMode || appData.matchGames) chessProgram = TRUE;
\r
1709 if (appData.icsActive || appData.noChessProgram) {
\r
1710 chessProgram = FALSE; /* not local chess program mode */
\r
1713 /* Open startup dialog if needed */
\r
1714 if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||
\r
1715 (appData.icsActive && *appData.icsHost == NULLCHAR) ||
\r
1716 (chessProgram && (*appData.firstChessProgram == NULLCHAR ||
\r
1717 *appData.secondChessProgram == NULLCHAR))) {
\r
1720 lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
\r
1721 DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
\r
1722 FreeProcInstance(lpProc);
\r
1725 /* Make sure save files land in the right (?) directory */
\r
1726 if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {
\r
1727 appData.saveGameFile = strdup(buf);
\r
1729 if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {
\r
1730 appData.savePositionFile = strdup(buf);
\r
1733 /* Finish initialization for fonts and sounds */
\r
1734 for (i=0; i<NUM_FONTS; i++) {
\r
1735 for (j=0; j<NUM_SIZES; j++) {
\r
1736 CreateFontInMF(font[j][i]);
\r
1739 /* xboard, and older WinBoards, controlled the move sound with the
\r
1740 appData.ringBellAfterMoves option. In the current WinBoard, we
\r
1741 always turn the option on (so that the backend will call us),
\r
1742 then let the user turn the sound off by setting it to silence if
\r
1743 desired. To accommodate old winboard.ini files saved by old
\r
1744 versions of WinBoard, we also turn off the sound if the option
\r
1745 was initially set to false. */
\r
1746 if (!appData.ringBellAfterMoves) {
\r
1747 sounds[(int)SoundMove].name = strdup("");
\r
1748 appData.ringBellAfterMoves = TRUE;
\r
1750 GetCurrentDirectory(MSG_SIZ, currDir);
\r
1751 SetCurrentDirectory(installDir);
\r
1753 SetCurrentDirectory(currDir);
\r
1755 p = icsTextMenuString;
\r
1756 if (p[0] == '@') {
\r
1757 FILE* f = fopen(p + 1, "r");
\r
1759 DisplayFatalError(p + 1, errno, 2);
\r
1762 i = fread(buf, 1, sizeof(buf)-1, f);
\r
1764 buf[i] = NULLCHAR;
\r
1767 ParseIcsTextMenu(strdup(p));
\r
1774 HMENU hmenu = GetMenu(hwndMain);
\r
1776 (void) EnableMenuItem(hmenu, IDM_CommPort,
\r
1777 MF_BYCOMMAND|((appData.icsActive &&
\r
1778 *appData.icsCommPort != NULLCHAR) ?
\r
1779 MF_ENABLED : MF_GRAYED));
\r
1780 (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
\r
1781 MF_BYCOMMAND|(saveSettingsOnExit ?
\r
1782 MF_CHECKED : MF_UNCHECKED));
\r
1787 SaveSettings(char* name)
\r
1790 ArgDescriptor *ad;
\r
1791 WINDOWPLACEMENT wp;
\r
1792 char dir[MSG_SIZ];
\r
1794 if (!hwndMain) return;
\r
1796 GetCurrentDirectory(MSG_SIZ, dir);
\r
1797 SetCurrentDirectory(installDir);
\r
1798 f = fopen(name, "w");
\r
1799 SetCurrentDirectory(dir);
\r
1801 DisplayError(name, errno);
\r
1804 fprintf(f, ";\n");
\r
1805 fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);
\r
1806 fprintf(f, ";\n");
\r
1807 fprintf(f, "; You can edit the values of options that are already set in this file,\n");
\r
1808 fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");
\r
1809 fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");
\r
1810 fprintf(f, ";\n");
\r
1812 wp.length = sizeof(WINDOWPLACEMENT);
\r
1813 GetWindowPlacement(hwndMain, &wp);
\r
1814 boardX = wp.rcNormalPosition.left;
\r
1815 boardY = wp.rcNormalPosition.top;
\r
1817 if (hwndConsole) {
\r
1818 GetWindowPlacement(hwndConsole, &wp);
\r
1819 consoleX = wp.rcNormalPosition.left;
\r
1820 consoleY = wp.rcNormalPosition.top;
\r
1821 consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
\r
1822 consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
\r
1825 if (analysisDialog) {
\r
1826 GetWindowPlacement(analysisDialog, &wp);
\r
1827 analysisX = wp.rcNormalPosition.left;
\r
1828 analysisY = wp.rcNormalPosition.top;
\r
1829 analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
\r
1830 analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
\r
1833 if (commentDialog) {
\r
1834 GetWindowPlacement(commentDialog, &wp);
\r
1835 commentX = wp.rcNormalPosition.left;
\r
1836 commentY = wp.rcNormalPosition.top;
\r
1837 commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
\r
1838 commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
\r
1841 if (editTagsDialog) {
\r
1842 GetWindowPlacement(editTagsDialog, &wp);
\r
1843 editTagsX = wp.rcNormalPosition.left;
\r
1844 editTagsY = wp.rcNormalPosition.top;
\r
1845 editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
\r
1846 editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
\r
1849 if (gameListDialog) {
\r
1850 GetWindowPlacement(gameListDialog, &wp);
\r
1851 gameListX = wp.rcNormalPosition.left;
\r
1852 gameListY = wp.rcNormalPosition.top;
\r
1853 gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
\r
1854 gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
\r
1857 for (ad = argDescriptors; ad->argName != NULL; ad++) {
\r
1858 if (!ad->save) continue;
\r
1859 switch (ad->argType) {
\r
1862 char *p = *(char **)ad->argLoc;
\r
1863 if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {
\r
1864 /* Quote multiline values or \-containing values
\r
1865 with { } if possible */
\r
1866 fprintf(f, "/%s={%s}\n", ad->argName, p);
\r
1868 /* Else quote with " " */
\r
1869 fprintf(f, "/%s=\"", ad->argName);
\r
1871 if (*p == '\n') fprintf(f, "\n");
\r
1872 else if (*p == '\r') fprintf(f, "\\r");
\r
1873 else if (*p == '\t') fprintf(f, "\\t");
\r
1874 else if (*p == '\b') fprintf(f, "\\b");
\r
1875 else if (*p == '\f') fprintf(f, "\\f");
\r
1876 else if (*p < ' ') fprintf(f, "\\%03o", *p);
\r
1877 else if (*p == '\"') fprintf(f, "\\\"");
\r
1878 else if (*p == '\\') fprintf(f, "\\\\");
\r
1882 fprintf(f, "\"\n");
\r
1887 fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);
\r
1890 fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);
\r
1893 fprintf(f, "/%s=%s\n", ad->argName,
\r
1894 (*(Boolean *)ad->argLoc) ? "true" : "false");
\r
1897 if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
\r
1900 if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
\r
1904 COLORREF color = *(COLORREF *)ad->argLoc;
\r
1905 fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName,
\r
1906 color&0xff, (color>>8)&0xff, (color>>16)&0xff);
\r
1911 MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
\r
1912 fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,
\r
1913 (ta->effects & CFE_BOLD) ? "b" : "",
\r
1914 (ta->effects & CFE_ITALIC) ? "i" : "",
\r
1915 (ta->effects & CFE_UNDERLINE) ? "u" : "",
\r
1916 (ta->effects & CFE_STRIKEOUT) ? "s" : "",
\r
1917 (ta->effects) ? " " : "",
\r
1918 ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
\r
1922 if (strchr(*(char **)ad->argLoc, '\"')) {
\r
1923 fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);
\r
1925 fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);
\r
1928 case ArgBoardSize:
\r
1929 fprintf(f, "/%s=%s\n", ad->argName,
\r
1930 sizeInfo[*(BoardSize *)ad->argLoc].name);
\r
1935 for (bs=0; bs<NUM_SIZES; bs++) {
\r
1936 MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
\r
1937 fprintf(f, "/size=%s ", sizeInfo[bs].name);
\r
1938 fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",
\r
1939 ad->argName, mfp->faceName, mfp->pointSize,
\r
1940 mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
\r
1941 mfp->bold ? "b" : "",
\r
1942 mfp->italic ? "i" : "",
\r
1943 mfp->underline ? "u" : "",
\r
1944 mfp->strikeout ? "s" : "");
\r
1948 case ArgCommSettings:
\r
1949 PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);
\r
1957 /*---------------------------------------------------------------------------*\
\r
1959 * GDI board drawing routines
\r
1961 \*---------------------------------------------------------------------------*/
\r
1964 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
\r
1968 sprintf(name, "%s%d%s", piece, squareSize, suffix);
\r
1969 if (gameInfo.event &&
\r
1970 strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
\r
1971 strcmp(name, "k80s") == 0) {
\r
1972 strcpy(name, "tim");
\r
1974 return LoadBitmap(hinst, name);
\r
1978 /* Insert a color into the program's logical palette
\r
1979 structure. This code assumes the given color is
\r
1980 the result of the RGB or PALETTERGB macro, and it
\r
1981 knows how those macros work (which is documented).
\r
1984 InsertInPalette(COLORREF color)
\r
1986 LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
\r
1988 if (pLogPal->palNumEntries++ >= PALETTESIZE) {
\r
1989 DisplayFatalError("Too many colors", 0, 1);
\r
1990 pLogPal->palNumEntries--;
\r
1994 pe->peFlags = (char) 0;
\r
1995 pe->peRed = (char) (0xFF & color);
\r
1996 pe->peGreen = (char) (0xFF & (color >> 8));
\r
1997 pe->peBlue = (char) (0xFF & (color >> 16));
\r
2003 InitDrawingColors()
\r
2005 if (pLogPal == NULL) {
\r
2006 /* Allocate enough memory for a logical palette with
\r
2007 * PALETTESIZE entries and set the size and version fields
\r
2008 * of the logical palette structure.
\r
2010 pLogPal = (NPLOGPALETTE)
\r
2011 LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
\r
2012 (sizeof(PALETTEENTRY) * (PALETTESIZE))));
\r
2013 pLogPal->palVersion = 0x300;
\r
2015 pLogPal->palNumEntries = 0;
\r
2017 InsertInPalette(lightSquareColor);
\r
2018 InsertInPalette(darkSquareColor);
\r
2019 InsertInPalette(whitePieceColor);
\r
2020 InsertInPalette(blackPieceColor);
\r
2021 InsertInPalette(highlightSquareColor);
\r
2022 InsertInPalette(premoveHighlightColor);
\r
2024 /* create a logical color palette according the information
\r
2025 * in the LOGPALETTE structure.
\r
2027 hPal = CreatePalette((LPLOGPALETTE) pLogPal);
\r
2029 lightSquareBrush = CreateSolidBrush(lightSquareColor);
\r
2030 darkSquareBrush = CreateSolidBrush(darkSquareColor);
\r
2031 whitePieceBrush = CreateSolidBrush(whitePieceColor);
\r
2032 blackPieceBrush = CreateSolidBrush(blackPieceColor);
\r
2033 iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
\r
2038 BoardWidth(int boardSize)
\r
2040 return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +
\r
2041 BOARD_SIZE * sizeInfo[boardSize].squareSize;
\r
2044 /* Respond to board resize by dragging edge */
\r
2046 ResizeBoard(int newSizeX, int newSizeY, int flags)
\r
2048 BoardSize newSize = NUM_SIZES - 1;
\r
2049 static int recurse = 0;
\r
2050 if (IsIconic(hwndMain)) return;
\r
2051 if (recurse > 0) return;
\r
2053 while (newSize > 0 &&
\r
2054 (newSizeX < sizeInfo[newSize].cliWidth ||
\r
2055 newSizeY < sizeInfo[newSize].cliHeight)) {
\r
2058 boardSize = newSize;
\r
2059 InitDrawingSizes(boardSize, flags);
\r
2066 InitDrawingSizes(BoardSize boardSize, int flags)
\r
2068 int i, boardWidth;
\r
2069 ChessSquare piece;
\r
2070 static int oldBoardSize = -1, oldTinyLayout = 0;
\r
2072 SIZE clockSize, messageSize;
\r
2074 char buf[MSG_SIZ];
\r
2076 HMENU hmenu = GetMenu(hwndMain);
\r
2077 RECT crect, wrect;
\r
2079 LOGBRUSH logbrush;
\r
2081 tinyLayout = sizeInfo[boardSize].tinyLayout;
\r
2082 smallLayout = sizeInfo[boardSize].smallLayout;
\r
2083 squareSize = sizeInfo[boardSize].squareSize;
\r
2084 lineGap = sizeInfo[boardSize].lineGap;
\r
2086 if (tinyLayout != oldTinyLayout) {
\r
2087 long style = GetWindowLong(hwndMain, GWL_STYLE);
\r
2089 style &= ~WS_SYSMENU;
\r
2090 InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
\r
2091 "&Minimize\tCtrl+F4");
\r
2093 style |= WS_SYSMENU;
\r
2094 RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
\r
2096 SetWindowLong(hwndMain, GWL_STYLE, style);
\r
2098 for (i=0; menuBarText[tinyLayout][i]; i++) {
\r
2099 ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP,
\r
2100 (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
\r
2102 DrawMenuBar(hwndMain);
\r
2105 boardWidth = BoardWidth(boardSize);
\r
2107 /* Get text area sizes */
\r
2108 hdc = GetDC(hwndMain);
\r
2109 if (appData.clockMode) {
\r
2110 sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
\r
2112 sprintf(buf, "White");
\r
2114 oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
\r
2115 GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
\r
2116 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2117 str = "We only care about the height here";
\r
2118 GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
\r
2119 SelectObject(hdc, oldFont);
\r
2120 ReleaseDC(hwndMain, hdc);
\r
2122 /* Compute where everything goes */
\r
2123 whiteRect.left = OUTER_MARGIN;
\r
2124 whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
\r
2125 whiteRect.top = OUTER_MARGIN;
\r
2126 whiteRect.bottom = whiteRect.top + clockSize.cy;
\r
2128 blackRect.left = whiteRect.right + INNER_MARGIN;
\r
2129 blackRect.right = blackRect.left + boardWidth/2 - 1;
\r
2130 blackRect.top = whiteRect.top;
\r
2131 blackRect.bottom = whiteRect.bottom;
\r
2133 messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;
\r
2134 if (appData.showButtonBar) {
\r
2135 messageRect.right = blackRect.right
\r
2136 - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
\r
2138 messageRect.right = blackRect.right;
\r
2140 messageRect.top = whiteRect.bottom + INNER_MARGIN;
\r
2141 messageRect.bottom = messageRect.top + messageSize.cy;
\r
2143 boardRect.left = whiteRect.left;
\r
2144 boardRect.right = boardRect.left + boardWidth;
\r
2145 boardRect.top = messageRect.bottom + INNER_MARGIN;
\r
2146 boardRect.bottom = boardRect.top + boardWidth;
\r
2148 sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
\r
2149 sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
\r
2150 winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
\r
2151 winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
\r
2152 GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
\r
2153 GetWindowRect(hwndMain, &wrect);
\r
2154 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
\r
2155 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2156 /* compensate if menu bar wrapped */
\r
2157 GetClientRect(hwndMain, &crect);
\r
2158 offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
\r
2159 winHeight += offby;
\r
2161 case WMSZ_TOPLEFT:
\r
2162 SetWindowPos(hwndMain, NULL,
\r
2163 wrect.right - winWidth, wrect.bottom - winHeight,
\r
2164 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2167 case WMSZ_TOPRIGHT:
\r
2169 SetWindowPos(hwndMain, NULL,
\r
2170 wrect.left, wrect.bottom - winHeight,
\r
2171 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2174 case WMSZ_BOTTOMLEFT:
\r
2176 SetWindowPos(hwndMain, NULL,
\r
2177 wrect.right - winWidth, wrect.top,
\r
2178 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
\r
2181 case WMSZ_BOTTOMRIGHT:
\r
2185 SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
\r
2186 SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
\r
2191 for (i = 0; i < N_BUTTONS; i++) {
\r
2192 if (buttonDesc[i].hwnd != NULL) {
\r
2193 DestroyWindow(buttonDesc[i].hwnd);
\r
2194 buttonDesc[i].hwnd = NULL;
\r
2196 if (appData.showButtonBar) {
\r
2197 buttonDesc[i].hwnd =
\r
2198 CreateWindow("BUTTON", buttonDesc[i].label,
\r
2199 WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
\r
2200 boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
\r
2201 messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
\r
2202 (HMENU) buttonDesc[i].id,
\r
2203 (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
\r
2205 SendMessage(buttonDesc[i].hwnd, WM_SETFONT,
\r
2206 (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
\r
2207 MAKELPARAM(FALSE, 0));
\r
2209 if (buttonDesc[i].id == IDM_Pause)
\r
2210 hwndPause = buttonDesc[i].hwnd;
\r
2211 buttonDesc[i].wndproc = (WNDPROC)
\r
2212 SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
\r
2215 if (gridPen != NULL) DeleteObject(gridPen);
\r
2216 if (highlightPen != NULL) DeleteObject(highlightPen);
\r
2217 if (premovePen != NULL) DeleteObject(premovePen);
\r
2218 if (lineGap != 0) {
\r
2219 logbrush.lbStyle = BS_SOLID;
\r
2220 logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
\r
2222 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2223 lineGap, &logbrush, 0, NULL);
\r
2224 logbrush.lbColor = highlightSquareColor;
\r
2226 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2227 lineGap, &logbrush, 0, NULL);
\r
2229 logbrush.lbColor = premoveHighlightColor;
\r
2231 ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
\r
2232 lineGap, &logbrush, 0, NULL);
\r
2234 for (i = 0; i < BOARD_SIZE + 1; i++) {
\r
2235 gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
\r
2236 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;
\r
2237 gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
\r
2238 boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
\r
2239 gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
\r
2240 BOARD_SIZE * (squareSize + lineGap);
\r
2241 gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =
\r
2242 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +
\r
2243 lineGap / 2 + (i * (squareSize + lineGap));
\r
2244 gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =
\r
2245 boardRect.top + BOARD_SIZE * (squareSize + lineGap);
\r
2246 gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
\r
2250 if (boardSize == oldBoardSize) return;
\r
2251 oldBoardSize = boardSize;
\r
2252 oldTinyLayout = tinyLayout;
\r
2254 /* Load piece bitmaps for this board size */
\r
2255 for (i=0; i<=2; i++) {
\r
2256 for (piece = WhitePawn;
\r
2257 (int) piece <= (int) WhiteKing;
\r
2258 piece = (ChessSquare) ((int) piece + 1)) {
\r
2259 if (pieceBitmap[i][piece] != NULL)
\r
2260 DeleteObject(pieceBitmap[i][piece]);
\r
2264 pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
\r
2265 pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
\r
2266 pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
\r
2267 pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
\r
2268 pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
\r
2269 pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
\r
2270 pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
\r
2271 pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
\r
2272 pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
\r
2273 pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
\r
2274 pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
\r
2275 pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
\r
2276 pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
\r
2277 pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
\r
2278 pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
\r
2279 pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
\r
2280 pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
\r
2281 pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
\r
2286 PieceBitmap(ChessSquare p, int kind)
\r
2288 if ((int) p >= (int) BlackPawn)
\r
2289 p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
\r
2291 return pieceBitmap[kind][(int) p];
\r
2294 /***************************************************************/
\r
2296 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
\r
2297 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
\r
2299 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
\r
2300 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
\r
2304 SquareToPos(int row, int column, int * x, int * y)
\r
2307 *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
\r
2308 *y = boardRect.top + lineGap + row * (squareSize + lineGap);
\r
2310 *x = boardRect.left + lineGap + column * (squareSize + lineGap);
\r
2311 *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
\r
2316 DrawCoordsOnDC(HDC hdc)
\r
2318 static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};
\r
2319 static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};
\r
2320 char str[2] = { NULLCHAR, NULLCHAR };
\r
2321 int oldMode, oldAlign, x, y, start, i;
\r
2325 if (!appData.showCoords)
\r
2328 start = flipView ? 0 : 8;
\r
2330 oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
\r
2331 oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
\r
2332 oldAlign = GetTextAlign(hdc);
\r
2333 oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
\r
2335 y = boardRect.top + lineGap;
\r
2336 x = boardRect.left + lineGap;
\r
2338 SetTextAlign(hdc, TA_LEFT|TA_TOP);
\r
2339 for (i = 0; i < 8; i++) {
\r
2340 str[0] = files[start + i];
\r
2341 ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
\r
2342 y += squareSize + lineGap;
\r
2345 SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
\r
2346 for (i = 0; i < 8; i++) {
\r
2347 str[0] = ranks[start + i];
\r
2348 ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
\r
2349 x += squareSize + lineGap;
\r
2352 SelectObject(hdc, oldBrush);
\r
2353 SetBkMode(hdc, oldMode);
\r
2354 SetTextAlign(hdc, oldAlign);
\r
2355 SelectObject(hdc, oldFont);
\r
2359 DrawGridOnDC(HDC hdc)
\r
2363 if (lineGap != 0) {
\r
2364 oldPen = SelectObject(hdc, gridPen);
\r
2365 PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);
\r
2366 SelectObject(hdc, oldPen);
\r
2370 #define HIGHLIGHT_PEN 0
\r
2371 #define PREMOVE_PEN 1
\r
2374 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
\r
2377 HPEN oldPen, hPen;
\r
2378 if (lineGap == 0) return;
\r
2380 x1 = boardRect.left +
\r
2381 lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);
\r
2382 y1 = boardRect.top +
\r
2383 lineGap/2 + y * (squareSize + lineGap);
\r
2385 x1 = boardRect.left +
\r
2386 lineGap/2 + x * (squareSize + lineGap);
\r
2387 y1 = boardRect.top +
\r
2388 lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);
\r
2390 hPen = pen ? premovePen : highlightPen;
\r
2391 oldPen = SelectObject(hdc, on ? hPen : gridPen);
\r
2392 MoveToEx(hdc, x1, y1, NULL);
\r
2393 LineTo(hdc, x1 + squareSize + lineGap, y1);
\r
2394 LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
\r
2395 LineTo(hdc, x1, y1 + squareSize + lineGap);
\r
2396 LineTo(hdc, x1, y1);
\r
2397 SelectObject(hdc, oldPen);
\r
2401 DrawHighlightsOnDC(HDC hdc)
\r
2404 for (i=0; i<2; i++) {
\r
2405 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0)
\r
2406 DrawHighlightOnDC(hdc, TRUE,
\r
2407 highlightInfo.sq[i].x, highlightInfo.sq[i].y,
\r
2410 for (i=0; i<2; i++) {
\r
2411 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2412 premoveHighlightInfo.sq[i].y >= 0) {
\r
2413 DrawHighlightOnDC(hdc, TRUE,
\r
2414 premoveHighlightInfo.sq[i].x,
\r
2415 premoveHighlightInfo.sq[i].y,
\r
2421 /* Note: sqcolor is used only in monoMode */
\r
2422 /* Note that this code is largely duplicated in woptions.c,
\r
2423 function DrawSampleSquare, so that needs to be updated too */
\r
2425 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
\r
2427 HBITMAP oldBitmap;
\r
2430 if (appData.blindfold) return;
\r
2432 if (appData.monoMode) {
\r
2433 SelectObject(tmphdc, PieceBitmap(piece,
\r
2434 color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
\r
2435 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
\r
2436 sqcolor ? SRCCOPY : NOTSRCCOPY);
\r
2439 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
\r
2440 oldBrush = SelectObject(hdc, whitePieceBrush);
\r
2441 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
\r
2443 /* Use black piece color for outline of white pieces */
\r
2444 /* Not sure this looks really good (though xboard does it).
\r
2445 Maybe better to have another selectable color, default black */
\r
2446 SelectObject(hdc, blackPieceBrush); /* could have own brush */
\r
2447 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2448 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
\r
2450 /* Use black for outline of white pieces */
\r
2451 SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
\r
2452 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);
\r
2456 /* Use white piece color for details of black pieces */
\r
2457 /* Requires filled-in solid bitmaps (BLACK_PIECE class); the
\r
2458 WHITE_PIECE ones aren't always the right shape. */
\r
2459 /* Not sure this looks really good (though xboard does it).
\r
2460 Maybe better to have another selectable color, default medium gray? */
\r
2461 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));
\r
2462 oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */
\r
2463 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
\r
2464 SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2465 SelectObject(hdc, blackPieceBrush);
\r
2466 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
\r
2468 /* Use square color for details of black pieces */
\r
2469 oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
\r
2470 oldBrush = SelectObject(hdc, blackPieceBrush);
\r
2471 BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
\r
2474 SelectObject(hdc, oldBrush);
\r
2475 SelectObject(tmphdc, oldBitmap);
\r
2480 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
\r
2482 int row, column, x, y, square_color, piece_color;
\r
2483 ChessSquare piece;
\r
2486 for (row = 0; row < BOARD_SIZE; row++) {
\r
2487 for (column = 0; column < BOARD_SIZE; column++) {
\r
2489 SquareToPos(row, column, &x, &y);
\r
2491 piece = board[row][column];
\r
2493 square_color = ((column + row) % 2) == 1;
\r
2494 piece_color = (int) piece < (int) BlackPawn;
\r
2496 if (appData.monoMode) {
\r
2497 if (piece == EmptySquare) {
\r
2498 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
\r
2499 square_color ? WHITENESS : BLACKNESS);
\r
2501 DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
\r
2504 oldBrush = SelectObject(hdc, square_color ?
\r
2505 lightSquareBrush : darkSquareBrush);
\r
2506 BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
\r
2507 SelectObject(hdc, oldBrush);
\r
2508 if (piece != EmptySquare)
\r
2509 DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
\r
2515 #define MAX_CLIPS 200 /* more than enough */
\r
2518 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
\r
2520 static Board lastReq, lastDrawn;
\r
2521 static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
\r
2522 static int lastDrawnFlipView = 0;
\r
2523 static int lastReqValid = 0, lastDrawnValid = 0;
\r
2524 int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
\r
2527 HBITMAP bufferBitmap;
\r
2528 HBITMAP oldBitmap;
\r
2530 HRGN clips[MAX_CLIPS];
\r
2531 ChessSquare dragged_piece = EmptySquare;
\r
2533 /* I'm undecided on this - this function figures out whether a full
\r
2534 * repaint is necessary on its own, so there's no real reason to have the
\r
2535 * caller tell it that. I think this can safely be set to FALSE - but
\r
2536 * if we trust the callers not to request full repaints unnessesarily, then
\r
2537 * we could skip some clipping work. In other words, only request a full
\r
2538 * redraw when the majority of pieces have changed positions (ie. flip,
\r
2539 * gamestart and similar) --Hawk
\r
2541 Boolean fullrepaint = repaint;
\r
2543 if (board == NULL) {
\r
2544 if (!lastReqValid) {
\r
2549 CopyBoard(lastReq, board);
\r
2553 if (doingSizing) {
\r
2557 if (IsIconic(hwndMain)) {
\r
2561 if (hdc == NULL) {
\r
2562 hdc = GetDC(hwndMain);
\r
2563 if (!appData.monoMode) {
\r
2564 SelectPalette(hdc, hPal, FALSE);
\r
2565 RealizePalette(hdc);
\r
2569 releaseDC = FALSE;
\r
2573 fprintf(debugFP, "*******************************\n"
\r
2575 "dragInfo.from (%d,%d)\n"
\r
2576 "dragInfo.start (%d,%d)\n"
\r
2577 "dragInfo.pos (%d,%d)\n"
\r
2578 "dragInfo.lastpos (%d,%d)\n",
\r
2579 repaint ? "TRUE" : "FALSE",
\r
2580 dragInfo.from.x, dragInfo.from.y,
\r
2581 dragInfo.start.x, dragInfo.start.y,
\r
2582 dragInfo.pos.x, dragInfo.pos.y,
\r
2583 dragInfo.lastpos.x, dragInfo.lastpos.y);
\r
2584 fprintf(debugFP, "prev: ");
\r
2585 for (row = 0; row < 8; row++) {
\r
2586 for (column = 0; column < 8; column++) {
\r
2587 fprintf(debugFP, "%d ", lastDrawn[row][column]);
\r
2590 fprintf(debugFP, "\n");
\r
2591 fprintf(debugFP, "board: ");
\r
2592 for (row = 0; row < 8; row++) {
\r
2593 for (column = 0; column < 8; column++) {
\r
2594 fprintf(debugFP, "%d ", board[row][column]);
\r
2597 fprintf(debugFP, "\n");
\r
2601 /* Create some work-DCs */
\r
2602 hdcmem = CreateCompatibleDC(hdc);
\r
2603 tmphdc = CreateCompatibleDC(hdc);
\r
2605 /* Figure out which squares need updating by comparing the
\r
2606 * newest board with the last drawn board and checking if
\r
2607 * flipping has changed.
\r
2609 if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
\r
2610 for (row = 0; row < 8; row++) {
\r
2611 for (column = 0; column < 8; column++) {
\r
2612 if (lastDrawn[row][column] != board[row][column]) {
\r
2613 SquareToPos(row, column, &x, &y);
\r
2614 clips[num_clips++] =
\r
2615 CreateRectRgn(x, y, x + squareSize, y + squareSize);
\r
2619 for (i=0; i<2; i++) {
\r
2620 if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
\r
2621 lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
\r
2622 if (lastDrawnHighlight.sq[i].x >= 0 &&
\r
2623 lastDrawnHighlight.sq[i].y >= 0) {
\r
2624 SquareToPos(lastDrawnHighlight.sq[i].y,
\r
2625 lastDrawnHighlight.sq[i].x, &x, &y);
\r
2626 clips[num_clips++] =
\r
2627 CreateRectRgn(x - lineGap, y - lineGap,
\r
2628 x + squareSize + lineGap, y + squareSize + lineGap);
\r
2630 if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
\r
2631 SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
\r
2632 clips[num_clips++] =
\r
2633 CreateRectRgn(x - lineGap, y - lineGap,
\r
2634 x + squareSize + lineGap, y + squareSize + lineGap);
\r
2638 for (i=0; i<2; i++) {
\r
2639 if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
\r
2640 lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
\r
2641 if (lastDrawnPremove.sq[i].x >= 0 &&
\r
2642 lastDrawnPremove.sq[i].y >= 0) {
\r
2643 SquareToPos(lastDrawnPremove.sq[i].y,
\r
2644 lastDrawnPremove.sq[i].x, &x, &y);
\r
2645 clips[num_clips++] =
\r
2646 CreateRectRgn(x - lineGap, y - lineGap,
\r
2647 x + squareSize + lineGap, y + squareSize + lineGap);
\r
2649 if (premoveHighlightInfo.sq[i].x >= 0 &&
\r
2650 premoveHighlightInfo.sq[i].y >= 0) {
\r
2651 SquareToPos(premoveHighlightInfo.sq[i].y,
\r
2652 premoveHighlightInfo.sq[i].x, &x, &y);
\r
2653 clips[num_clips++] =
\r
2654 CreateRectRgn(x - lineGap, y - lineGap,
\r
2655 x + squareSize + lineGap, y + squareSize + lineGap);
\r
2660 fullrepaint = TRUE;
\r
2663 /* Create a buffer bitmap - this is the actual bitmap
\r
2664 * being written to. When all the work is done, we can
\r
2665 * copy it to the real DC (the screen). This avoids
\r
2666 * the problems with flickering.
\r
2668 GetClientRect(hwndMain, &Rect);
\r
2669 bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
\r
2670 Rect.bottom-Rect.top+1);
\r
2671 oldBitmap = SelectObject(hdcmem, bufferBitmap);
\r
2672 if (!appData.monoMode) {
\r
2673 SelectPalette(hdcmem, hPal, FALSE);
\r
2676 /* Create clips for dragging */
\r
2677 if (!fullrepaint) {
\r
2678 if (dragInfo.from.x >= 0) {
\r
2679 SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
\r
2680 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
2682 if (dragInfo.start.x >= 0) {
\r
2683 SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
\r
2684 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
2686 if (dragInfo.pos.x >= 0) {
\r
2687 x = dragInfo.pos.x - squareSize / 2;
\r
2688 y = dragInfo.pos.y - squareSize / 2;
\r
2689 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
2691 if (dragInfo.lastpos.x >= 0) {
\r
2692 x = dragInfo.lastpos.x - squareSize / 2;
\r
2693 y = dragInfo.lastpos.y - squareSize / 2;
\r
2694 clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
\r
2698 /* If dragging is in progress, we temporarely remove the piece */
\r
2699 if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
\r
2700 dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
\r
2701 board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
\r
2704 /* Are we animating a move?
\r
2706 * - remove the piece from the board (temporarely)
\r
2707 * - calculate the clipping region
\r
2709 if (!fullrepaint) {
\r
2710 if (animInfo.piece != EmptySquare) {
\r
2711 board[animInfo.from.y][animInfo.from.x] = EmptySquare;
\r
2712 x = boardRect.left + animInfo.lastpos.x;
\r
2713 y = boardRect.top + animInfo.lastpos.y;
\r
2714 x2 = boardRect.left + animInfo.pos.x;
\r
2715 y2 = boardRect.top + animInfo.pos.y;
\r
2716 clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
\r
2717 /* Slight kludge. The real problem is that after AnimateMove is
\r
2718 done, the position on the screen does not match lastDrawn.
\r
2719 This currently causes trouble only on e.p. captures in
\r
2720 atomic, where the piece moves to an empty square and then
\r
2721 explodes. The old and new positions both had an empty square
\r
2722 at the destination, but animation has drawn a piece there and
\r
2723 we have to remember to erase it. */
\r
2724 lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
\r
2728 /* No clips? Make sure we have fullrepaint set to TRUE */
\r
2729 if (num_clips == 0)
\r
2730 fullrepaint = TRUE;
\r
2732 /* Set clipping on the memory DC */
\r
2733 if (!fullrepaint) {
\r
2734 SelectClipRgn(hdcmem, clips[0]);
\r
2735 for (x = 1; x < num_clips; x++) {
\r
2736 if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
\r
2737 abort(); // this should never ever happen!
\r
2741 /* Do all the drawing to the memory DC */
\r
2742 DrawGridOnDC(hdcmem);
\r
2743 DrawHighlightsOnDC(hdcmem);
\r
2744 DrawBoardOnDC(hdcmem, board, tmphdc);
\r
2745 DrawCoordsOnDC(hdcmem);
\r
2747 /* Put the dragged piece back into place and draw it */
\r
2748 if (dragged_piece != EmptySquare) {
\r
2749 board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
\r
2750 x = dragInfo.pos.x - squareSize / 2;
\r
2751 y = dragInfo.pos.y - squareSize / 2;
\r
2752 DrawPieceOnDC(hdcmem, dragged_piece,
\r
2753 ((int) dragged_piece < (int) BlackPawn),
\r
2754 (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
\r
2757 /* Put the animated piece back into place and draw it */
\r
2758 if (animInfo.piece != EmptySquare) {
\r
2759 board[animInfo.from.y][animInfo.from.x] = animInfo.piece;
\r
2760 x = boardRect.left + animInfo.pos.x;
\r
2761 y = boardRect.top + animInfo.pos.y;
\r
2762 DrawPieceOnDC(hdcmem, animInfo.piece,
\r
2763 ((int) animInfo.piece < (int) BlackPawn),
\r
2764 (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
\r
2767 /* Release the bufferBitmap by selecting in the old bitmap
\r
2768 * and delete the memory DC
\r
2770 SelectObject(hdcmem, oldBitmap);
\r
2773 /* Set clipping on the target DC */
\r
2774 if (!fullrepaint) {
\r
2775 SelectClipRgn(hdc, clips[0]);
\r
2776 for (x = 1; x < num_clips; x++) {
\r
2777 if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
\r
2778 abort(); // this should never ever happen!
\r
2782 /* Copy the new bitmap onto the screen in one go.
\r
2783 * This way we avoid any flickering
\r
2785 oldBitmap = SelectObject(tmphdc, bufferBitmap);
\r
2786 BitBlt(hdc, boardRect.left, boardRect.top,
\r
2787 boardRect.right - boardRect.left,
\r
2788 boardRect.bottom - boardRect.top,
\r
2789 tmphdc, boardRect.left, boardRect.top, SRCCOPY);
\r
2790 SelectObject(tmphdc, oldBitmap);
\r
2792 /* Massive cleanup */
\r
2793 for (x = 0; x < num_clips; x++)
\r
2794 DeleteObject(clips[x]);
\r
2797 DeleteObject(bufferBitmap);
\r
2800 ReleaseDC(hwndMain, hdc);
\r
2802 if (lastDrawnFlipView != flipView) {
\r
2804 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
\r
2806 CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
\r
2809 CopyBoard(lastDrawn, board);
\r
2810 lastDrawnHighlight = highlightInfo;
\r
2811 lastDrawnPremove = premoveHighlightInfo;
\r
2812 lastDrawnFlipView = flipView;
\r
2813 lastDrawnValid = 1;
\r
2817 /*---------------------------------------------------------------------------*\
\r
2818 | CLIENT PAINT PROCEDURE
\r
2819 | This is the main event-handler for the WM_PAINT message.
\r
2821 \*---------------------------------------------------------------------------*/
\r
2823 PaintProc(HWND hwnd)
\r
2829 if(hdc = BeginPaint(hwnd, &ps)) {
\r
2830 if (IsIconic(hwnd)) {
\r
2831 DrawIcon(hdc, 2, 2, iconCurrent);
\r
2833 if (!appData.monoMode) {
\r
2834 SelectPalette(hdc, hPal, FALSE);
\r
2835 RealizePalette(hdc);
\r
2837 HDCDrawPosition(hdc, 1, NULL);
\r
2839 SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
\r
2840 ExtTextOut(hdc, messageRect.left, messageRect.top,
\r
2841 ETO_CLIPPED|ETO_OPAQUE,
\r
2842 &messageRect, messageText, strlen(messageText), NULL);
\r
2843 SelectObject(hdc, oldFont);
\r
2844 DisplayBothClocks();
\r
2846 EndPaint(hwnd,&ps);
\r
2854 * If the user selects on a border boundary, return -1; if off the board,
\r
2855 * return -2. Otherwise map the event coordinate to the square.
\r
2856 * The offset boardRect.left or boardRect.top must already have been
\r
2857 * subtracted from x.
\r
2860 EventToSquare(int x)
\r
2867 if ((x % (squareSize + lineGap)) >= squareSize)
\r
2869 x /= (squareSize + lineGap);
\r
2870 if (x >= BOARD_SIZE)
\r
2881 DropEnable dropEnables[] = {
\r
2882 { 'P', DP_Pawn, "Pawn" },
\r
2883 { 'N', DP_Knight, "Knight" },
\r
2884 { 'B', DP_Bishop, "Bishop" },
\r
2885 { 'R', DP_Rook, "Rook" },
\r
2886 { 'Q', DP_Queen, "Queen" },
\r
2890 SetupDropMenu(HMENU hmenu)
\r
2892 int i, count, enable;
\r
2894 extern char white_holding[], black_holding[];
\r
2895 char item[MSG_SIZ];
\r
2897 for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
\r
2898 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
\r
2899 dropEnables[i].piece);
\r
2901 while (p && *p++ == dropEnables[i].piece) count++;
\r
2902 sprintf(item, "%s %d", dropEnables[i].name, count);
\r
2903 enable = count > 0 || !appData.testLegality
\r
2904 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
\r
2905 && !appData.icsActive);
\r
2906 ModifyMenu(hmenu, dropEnables[i].command,
\r
2907 MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
\r
2908 dropEnables[i].command, item);
\r
2912 static int fromX = -1, fromY = -1, toX, toY;
\r
2914 /* Event handler for mouse messages */
\r
2916 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
2920 static int recursive = 0;
\r
2922 BOOLEAN saveAnimate;
\r
2923 static BOOLEAN sameAgain = FALSE;
\r
2926 if (message == WM_MBUTTONUP) {
\r
2927 /* Hideous kludge to fool TrackPopupMenu into paying attention
\r
2928 to the middle button: we simulate pressing the left button too!
\r
2930 PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
\r
2931 PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
\r
2937 pt.x = LOWORD(lParam);
\r
2938 pt.y = HIWORD(lParam);
\r
2939 x = EventToSquare(pt.x - boardRect.left);
\r
2940 y = EventToSquare(pt.y - boardRect.top);
\r
2941 if (!flipView && y >= 0) {
\r
2942 y = BOARD_SIZE - 1 - y;
\r
2944 if (flipView && x >= 0) {
\r
2945 x = BOARD_SIZE - 1 - x;
\r
2948 switch (message) {
\r
2949 case WM_LBUTTONDOWN:
\r
2951 sameAgain = FALSE;
\r
2953 /* Downclick vertically off board; check if on clock */
\r
2954 if (PtInRect((LPRECT) &whiteRect, pt)) {
\r
2955 if (gameMode == EditPosition) {
\r
2956 SetWhiteToPlayEvent();
\r
2957 } else if (gameMode == IcsPlayingBlack ||
\r
2958 gameMode == MachinePlaysWhite) {
\r
2961 } else if (PtInRect((LPRECT) &blackRect, pt)) {
\r
2962 if (gameMode == EditPosition) {
\r
2963 SetBlackToPlayEvent();
\r
2964 } else if (gameMode == IcsPlayingWhite ||
\r
2965 gameMode == MachinePlaysBlack) {
\r
2969 if (!appData.highlightLastMove) {
\r
2970 ClearHighlights();
\r
2971 DrawPosition(FALSE, NULL);
\r
2973 fromX = fromY = -1;
\r
2974 dragInfo.start.x = dragInfo.start.y = -1;
\r
2975 dragInfo.from = dragInfo.start;
\r
2977 } else if (x < 0 || y < 0) {
\r
2979 } else if (fromX == x && fromY == y) {
\r
2980 /* Downclick on same square again */
\r
2981 ClearHighlights();
\r
2982 DrawPosition(FALSE, NULL);
\r
2983 sameAgain = TRUE;
\r
2984 } else if (fromX != -1) {
\r
2985 /* Downclick on different square */
\r
2986 ChessSquare pdown, pup;
\r
2987 pdown = boards[currentMove][fromY][fromX];
\r
2988 pup = boards[currentMove][y][x];
\r
2989 if (gameMode == EditPosition ||
\r
2990 !((WhitePawn <= pdown && pdown <= WhiteKing &&
\r
2991 WhitePawn <= pup && pup <= WhiteKing) ||
\r
2992 (BlackPawn <= pdown && pdown <= BlackKing &&
\r
2993 BlackPawn <= pup && pup <= BlackKing))) {
\r
2994 /* EditPosition, empty square, or different color piece;
\r
2995 click-click move is possible */
\r
2998 if (IsPromotion(fromX, fromY, toX, toY)) {
\r
2999 if (appData.alwaysPromoteToQueen) {
\r
3000 UserMoveEvent(fromX, fromY, toX, toY, 'q');
\r
3001 if (!appData.highlightLastMove) {
\r
3002 ClearHighlights();
\r
3003 DrawPosition(FALSE, NULL);
\r
3006 SetHighlights(fromX, fromY, toX, toY);
\r
3007 DrawPosition(FALSE, NULL);
\r
3008 PromotionPopup(hwnd);
\r
3010 } else { /* not a promotion */
\r
3011 if (appData.animate || appData.highlightLastMove) {
\r
3012 SetHighlights(fromX, fromY, toX, toY);
\r
3014 ClearHighlights();
\r
3016 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
\r
3017 if (appData.animate && !appData.highlightLastMove) {
\r
3018 ClearHighlights();
\r
3019 DrawPosition(FALSE, NULL);
\r
3022 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
\r
3023 fromX = fromY = -1;
\r
3026 ClearHighlights();
\r
3027 DrawPosition(FALSE, NULL);
\r
3029 /* First downclick, or restart on a square with same color piece */
\r
3030 if (!frozen && OKToStartUserMove(x, y)) {
\r
3033 dragInfo.lastpos = pt;
\r
3034 dragInfo.from.x = fromX;
\r
3035 dragInfo.from.y = fromY;
\r
3036 dragInfo.start = dragInfo.from;
\r
3037 SetCapture(hwndMain);
\r
3039 fromX = fromY = -1;
\r
3040 dragInfo.start.x = dragInfo.start.y = -1;
\r
3041 dragInfo.from = dragInfo.start;
\r
3045 case WM_LBUTTONUP:
\r
3047 if (fromX == -1) break;
\r
3048 if (x == fromX && y == fromY) {
\r
3049 dragInfo.from.x = dragInfo.from.y = -1;
\r
3050 /* Upclick on same square */
\r
3052 /* Clicked same square twice: abort click-click move */
\r
3053 fromX = fromY = -1;
\r
3055 ClearPremoveHighlights();
\r
3057 /* First square clicked: start click-click move */
\r
3058 SetHighlights(fromX, fromY, -1, -1);
\r
3060 DrawPosition(FALSE, NULL);
\r
3061 } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {
\r
3062 /* Errant click; ignore */
\r
3065 /* Finish drag move */
\r
3066 dragInfo.from.x = dragInfo.from.y = -1;
\r
3069 saveAnimate = appData.animate; /* sorry, Hawk :) */
\r
3070 appData.animate = appData.animate && !appData.animateDragging;
\r
3071 if (IsPromotion(fromX, fromY, toX, toY)) {
\r
3072 if (appData.alwaysPromoteToQueen) {
\r
3073 UserMoveEvent(fromX, fromY, toX, toY, 'q');
\r
3075 DrawPosition(FALSE, NULL);
\r
3076 PromotionPopup(hwnd);
\r
3079 UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
\r
3081 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
\r
3082 appData.animate = saveAnimate;
\r
3083 fromX = fromY = -1;
\r
3084 if (appData.highlightDragging && !appData.highlightLastMove) {
\r
3085 ClearHighlights();
\r
3087 if (appData.animate || appData.animateDragging ||
\r
3088 appData.highlightDragging || gotPremove) {
\r
3089 DrawPosition(FALSE, NULL);
\r
3092 dragInfo.start.x = dragInfo.start.y = -1;
\r
3093 dragInfo.pos = dragInfo.lastpos = dragInfo.start;
\r
3096 case WM_MOUSEMOVE:
\r
3097 if ((appData.animateDragging || appData.highlightDragging)
\r
3098 && (wParam & MK_LBUTTON)
\r
3099 && dragInfo.from.x >= 0) {
\r
3100 if (appData.animateDragging) {
\r
3101 dragInfo.pos = pt;
\r
3103 if (appData.highlightDragging) {
\r
3104 SetHighlights(fromX, fromY, x, y);
\r
3106 DrawPosition(FALSE, NULL);
\r
3107 dragInfo.lastpos = dragInfo.pos;
\r
3111 case WM_MBUTTONDOWN:
\r
3112 case WM_RBUTTONDOWN:
\r
3115 fromX = fromY = -1;
\r
3116 dragInfo.pos.x = dragInfo.pos.y = -1;
\r
3117 dragInfo.start.x = dragInfo.start.y = -1;
\r
3118 dragInfo.from = dragInfo.start;
\r
3119 dragInfo.lastpos = dragInfo.pos;
\r
3120 if (appData.highlightDragging) {
\r
3121 ClearHighlights();
\r
3123 DrawPosition(TRUE, NULL);
\r
3125 switch (gameMode) {
\r
3126 case EditPosition:
\r
3127 case IcsExamining:
\r
3128 if (x < 0 || y < 0) break;
\r
3131 if (message == WM_MBUTTONDOWN) {
\r
3132 buttonCount = 3; /* even if system didn't think so */
\r
3133 if (wParam & MK_SHIFT)
\r
3134 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3136 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3137 } else { /* message == WM_RBUTTONDOWN */
\r
3139 if (buttonCount == 3) {
\r
3140 if (wParam & MK_SHIFT)
\r
3141 MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
\r
3143 MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
\r
3145 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3148 /* Just have one menu, on the right button. Windows users don't
\r
3149 think to try the middle one, and sometimes other software steals
\r
3150 it, or it doesn't really exist. */
\r
3151 MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
\r
3155 case IcsPlayingWhite:
\r
3156 case IcsPlayingBlack:
\r
3158 case MachinePlaysWhite:
\r
3159 case MachinePlaysBlack:
\r
3160 if (appData.testLegality &&
\r
3161 gameInfo.variant != VariantBughouse &&
\r
3162 gameInfo.variant != VariantCrazyhouse) break;
\r
3163 if (x < 0 || y < 0) break;
\r
3166 hmenu = LoadMenu(hInst, "DropPieceMenu");
\r
3167 SetupDropMenu(hmenu);
\r
3168 MenuPopup(hwnd, pt, hmenu, -1);
\r
3179 /* Preprocess messages for buttons in main window */
\r
3181 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3183 int id = GetWindowLong(hwnd, GWL_ID);
\r
3186 for (i=0; i<N_BUTTONS; i++) {
\r
3187 if (buttonDesc[i].id == id) break;
\r
3189 if (i == N_BUTTONS) return 0;
\r
3190 switch (message) {
\r
3195 dir = (wParam == VK_LEFT) ? -1 : 1;
\r
3196 SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
\r
3203 SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
\r
3206 if (appData.icsActive) {
\r
3207 if (GetKeyState(VK_SHIFT) < 0) {
\r
3209 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3210 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3214 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
3215 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3222 if (appData.icsActive) {
\r
3223 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3224 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3226 SendMessage(h, WM_CHAR, wParam, lParam);
\r
3228 } else if (isalpha((char)wParam) || isdigit((char)wParam)){
\r
3229 PopUpMoveDialog((char)wParam);
\r
3235 return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
\r
3238 /* Process messages for Promotion dialog box */
\r
3240 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
\r
3244 switch (message) {
\r
3245 case WM_INITDIALOG: /* message: initialize dialog box */
\r
3246 /* Center the dialog over the application window */
\r
3247 CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
\r
3248 ShowWindow(GetDlgItem(hDlg, PB_King),
\r
3249 (!appData.testLegality || gameInfo.variant == VariantSuicide ||
\r
3250 gameInfo.variant == VariantGiveaway) ?
\r
3251 SW_SHOW : SW_HIDE);
\r
3254 case WM_COMMAND: /* message: received a command */
\r
3255 switch (LOWORD(wParam)) {
\r
3257 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3258 ClearHighlights();
\r
3259 DrawPosition(FALSE, NULL);
\r
3279 EndDialog(hDlg, TRUE); /* Exit the dialog */
\r
3280 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
\r
3281 if (!appData.highlightLastMove) {
\r
3282 ClearHighlights();
\r
3283 DrawPosition(FALSE, NULL);
\r
3290 /* Pop up promotion dialog */
\r
3292 PromotionPopup(HWND hwnd)
\r
3296 lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
\r
3297 DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
\r
3298 hwnd, (DLGPROC)lpProc);
\r
3299 FreeProcInstance(lpProc);
\r
3302 /* Toggle ShowThinking */
\r
3304 ToggleShowThinking()
\r
3306 ShowThinkingEvent(!appData.showThinking);
\r
3310 LoadGameDialog(HWND hwnd, char* title)
\r
3314 char fileTitle[MSG_SIZ];
\r
3315 f = OpenFileDialog(hwnd, FALSE, "",
\r
3316 appData.oldSaveStyle ? "gam" : "pgn",
\r
3318 title, &number, fileTitle, NULL);
\r
3320 cmailMsgLoaded = FALSE;
\r
3321 if (number == 0) {
\r
3322 int error = GameListBuild(f);
\r
3324 DisplayError("Cannot build game list", error);
\r
3325 } else if (!ListEmpty(&gameList) &&
\r
3326 ((ListGame *) gameList.tailPred)->number > 1) {
\r
3327 GameListPopUp(f, fileTitle);
\r
3330 GameListDestroy();
\r
3333 LoadGame(f, number, fileTitle, FALSE);
\r
3338 ChangedConsoleFont()
\r
3341 CHARRANGE tmpsel, sel;
\r
3342 MyFont *f = font[boardSize][CONSOLE_FONT];
\r
3343 HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
3344 HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3347 cfmt.cbSize = sizeof(CHARFORMAT);
\r
3348 cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
\r
3349 strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
\r
3350 /* yHeight is expressed in twips. A twip is 1/20 of a font's point
\r
3351 * size. This was undocumented in the version of MSVC++ that I had
\r
3352 * when I wrote the code, but is apparently documented now.
\r
3354 cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
\r
3355 cfmt.bCharSet = f->lf.lfCharSet;
\r
3356 cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
\r
3357 SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
3358 SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt);
\r
3359 /* Why are the following seemingly needed too? */
\r
3360 SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
3361 SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt);
\r
3362 SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
\r
3364 tmpsel.cpMax = -1; /*999999?*/
\r
3365 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
\r
3366 SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt);
\r
3367 /* Trying putting this here too. It still seems to tickle a RichEdit
\r
3368 * bug: sometimes RichEdit indents the first line of a paragraph too.
\r
3370 paraf.cbSize = sizeof(paraf);
\r
3371 paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
\r
3372 paraf.dxStartIndent = 0;
\r
3373 paraf.dxOffset = WRAP_INDENT;
\r
3374 SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) ¶f);
\r
3375 SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
3378 /*---------------------------------------------------------------------------*\
\r
3380 * Window Proc for main window
\r
3382 \*---------------------------------------------------------------------------*/
\r
3384 /* Process messages for main window, etc. */
\r
3386 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
3389 int wmId, wmEvent;
\r
3393 char fileTitle[MSG_SIZ];
\r
3395 switch (message) {
\r
3397 case WM_PAINT: /* message: repaint portion of window */
\r
3401 case WM_ERASEBKGND:
\r
3402 if (IsIconic(hwnd)) {
\r
3403 /* Cheat; change the message */
\r
3404 return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
\r
3406 return (DefWindowProc(hwnd, message, wParam, lParam));
\r
3410 case WM_LBUTTONDOWN:
\r
3411 case WM_MBUTTONDOWN:
\r
3412 case WM_RBUTTONDOWN:
\r
3413 case WM_LBUTTONUP:
\r
3414 case WM_MBUTTONUP:
\r
3415 case WM_RBUTTONUP:
\r
3416 case WM_MOUSEMOVE:
\r
3417 MouseEvent(hwnd, message, wParam, lParam);
\r
3422 if (appData.icsActive) {
\r
3423 if (wParam == '\t') {
\r
3424 if (GetKeyState(VK_SHIFT) < 0) {
\r
3426 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3427 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3431 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
\r
3432 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3436 HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
\r
3437 if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
\r
3439 SendMessage(h, message, wParam, lParam);
\r
3441 } else if (isalpha((char)wParam) || isdigit((char)wParam)) {
\r
3442 PopUpMoveDialog((char)wParam);
\r
3446 case WM_PALETTECHANGED:
\r
3447 if (hwnd != (HWND)wParam && !appData.monoMode) {
\r
3449 HDC hdc = GetDC(hwndMain);
\r
3450 SelectPalette(hdc, hPal, TRUE);
\r
3451 nnew = RealizePalette(hdc);
\r
3453 paletteChanged = TRUE;
\r
3455 UpdateColors(hdc);
\r
3457 InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/
\r
3460 ReleaseDC(hwnd, hdc);
\r
3464 case WM_QUERYNEWPALETTE:
\r
3465 if (!appData.monoMode /*&& paletteChanged*/) {
\r
3467 HDC hdc = GetDC(hwndMain);
\r
3468 paletteChanged = FALSE;
\r
3469 SelectPalette(hdc, hPal, FALSE);
\r
3470 nnew = RealizePalette(hdc);
\r
3472 InvalidateRect(hwnd, &boardRect, FALSE);
\r
3474 ReleaseDC(hwnd, hdc);
\r
3479 case WM_COMMAND: /* message: command from application menu */
\r
3480 wmId = LOWORD(wParam);
\r
3481 wmEvent = HIWORD(wParam);
\r
3486 AnalysisPopDown();
\r
3489 case IDM_LoadGame:
\r
3490 LoadGameDialog(hwnd, "Load Game from File");
\r
3493 case IDM_LoadNextGame:
\r
3497 case IDM_LoadPrevGame:
\r
3501 case IDM_ReloadGame:
\r
3505 case IDM_LoadPosition:
\r
3506 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
\r
3507 Reset(FALSE, TRUE);
\r
3510 f = OpenFileDialog(hwnd, FALSE, "",
\r
3511 appData.oldSaveStyle ? "pos" : "fen",
\r
3513 "Load Position from File", &number, fileTitle, NULL);
\r
3515 LoadPosition(f, number, fileTitle);
\r
3519 case IDM_LoadNextPosition:
\r
3520 ReloadPosition(1);
\r
3523 case IDM_LoadPrevPosition:
\r
3524 ReloadPosition(-1);
\r
3527 case IDM_ReloadPosition:
\r
3528 ReloadPosition(0);
\r
3531 case IDM_SaveGame:
\r
3532 defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
\r
3533 f = OpenFileDialog(hwnd, TRUE, defName,
\r
3534 appData.oldSaveStyle ? "gam" : "pgn",
\r
3536 "Save Game to File", NULL, fileTitle, NULL);
\r
3538 SaveGame(f, 0, "");
\r
3542 case IDM_SavePosition:
\r
3543 defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
\r
3544 f = OpenFileDialog(hwnd, TRUE, defName,
\r
3545 appData.oldSaveStyle ? "pos" : "fen",
\r
3547 "Save Position to File", NULL, fileTitle, NULL);
\r
3549 SavePosition(f, 0, "");
\r
3553 case IDM_CopyGame:
\r
3554 CopyGameToClipboard();
\r
3557 case IDM_PasteGame:
\r
3558 PasteGameFromClipboard();
\r
3561 case IDM_CopyPosition:
\r
3562 CopyFENToClipboard();
\r
3565 case IDM_PastePosition:
\r
3566 PasteFENFromClipboard();
\r
3569 case IDM_MailMove:
\r
3573 case IDM_ReloadCMailMsg:
\r
3574 Reset(TRUE, TRUE);
\r
3575 ReloadCmailMsgEvent(FALSE);
\r
3578 case IDM_Minimize:
\r
3579 ShowWindow(hwnd, SW_MINIMIZE);
\r
3586 case IDM_MachineWhite:
\r
3587 MachineWhiteEvent();
\r
3589 * refresh the tags dialog only if it's visible
\r
3591 if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
\r
3593 tags = PGNTags(&gameInfo);
\r
3594 TagsPopUp(tags, CmailMsg());
\r
3599 case IDM_MachineBlack:
\r
3600 MachineBlackEvent();
\r
3602 * refresh the tags dialog only if it's visible
\r
3604 if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
\r
3606 tags = PGNTags(&gameInfo);
\r
3607 TagsPopUp(tags, CmailMsg());
\r
3612 case IDM_TwoMachines:
\r
3613 TwoMachinesEvent();
\r
3615 * refresh the tags dialog only if it's visible
\r
3617 if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
\r
3619 tags = PGNTags(&gameInfo);
\r
3620 TagsPopUp(tags, CmailMsg());
\r
3625 case IDM_AnalysisMode:
\r
3626 if (!first.analysisSupport) {
\r
3627 char buf[MSG_SIZ];
\r
3628 sprintf(buf, "%s does not support analysis", first.tidy);
\r
3629 DisplayError(buf, 0);
\r
3631 if (!appData.showThinking) ToggleShowThinking();
\r
3632 AnalyzeModeEvent();
\r
3636 case IDM_AnalyzeFile:
\r
3637 if (!first.analysisSupport) {
\r
3638 char buf[MSG_SIZ];
\r
3639 sprintf(buf, "%s does not support analysis", first.tidy);
\r
3640 DisplayError(buf, 0);
\r
3642 if (!appData.showThinking) ToggleShowThinking();
\r
3643 AnalyzeFileEvent();
\r
3644 LoadGameDialog(hwnd, "Analyze Game from File");
\r
3645 AnalysisPeriodicEvent(1);
\r
3649 case IDM_IcsClient:
\r
3653 case IDM_EditGame:
\r
3657 case IDM_EditPosition:
\r
3658 EditPositionEvent();
\r
3661 case IDM_Training:
\r
3665 case IDM_ShowGameList:
\r
3666 ShowGameListProc();
\r
3669 case IDM_EditTags:
\r
3673 case IDM_EditComment:
\r
3674 if (commentDialogUp && editComment) {
\r
3677 EditCommentEvent();
\r
3697 case IDM_CallFlag:
\r
3717 case IDM_StopObserving:
\r
3718 StopObservingEvent();
\r
3721 case IDM_StopExamining:
\r
3722 StopExaminingEvent();
\r
3725 case IDM_TypeInMove:
\r
3726 PopUpMoveDialog('\000');
\r
3729 case IDM_Backward:
\r
3731 SetFocus(hwndMain);
\r
3736 SetFocus(hwndMain);
\r
3741 SetFocus(hwndMain);
\r
3746 SetFocus(hwndMain);
\r
3753 case IDM_TruncateGame:
\r
3754 TruncateGameEvent();
\r
3761 case IDM_RetractMove:
\r
3762 RetractMoveEvent();
\r
3765 case IDM_FlipView:
\r
3766 flipView = !flipView;
\r
3767 DrawPosition(FALSE, NULL);
\r
3770 case IDM_GeneralOptions:
\r