Initial checkin. I created this by combining the XBoard 4.2.6 and
[xboard.git] / winboard / winboard.c
1 /* \r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id$\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\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
10  *\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
15  *\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
23  *\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
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\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
40  *\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
45  *\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
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 \r
57 #include <stdio.h>\r
58 #include <stdlib.h>\r
59 #include <malloc.h>\r
60 #include <io.h>\r
61 #include <sys/stat.h>\r
62 #include <fcntl.h>\r
63 #include <math.h>\r
64 #include <commdlg.h>\r
65 #include <dlgs.h>\r
66 #include <richedit.h>\r
67 \r
68 #if __GNUC__\r
69 #include <errno.h>\r
70 #include <string.h>\r
71 #endif\r
72 \r
73 #include "common.h"\r
74 #include "winboard.h"\r
75 #include "frontend.h"\r
76 #include "backend.h"\r
77 #include "moves.h"\r
78 #include "wclipbrd.h"\r
79 #include "wgamelist.h"\r
80 #include "wedittags.h"\r
81 #include "woptions.h"\r
82 #include "wsockerr.h"\r
83 #include "defaults.h"\r
84 \r
85 typedef struct {\r
86   ChessSquare piece;  \r
87   POINT pos;      /* window coordinates of current pos */\r
88   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
89   POINT from;     /* board coordinates of the piece's orig pos */\r
90   POINT to;       /* board coordinates of the piece's new pos */\r
91 } AnimInfo;\r
92 \r
93 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
94 \r
95 typedef struct {\r
96   POINT start;    /* window coordinates of start pos */\r
97   POINT pos;      /* window coordinates of current pos */\r
98   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
99   POINT from;     /* board coordinates of the piece's orig pos */\r
100 } DragInfo;\r
101 \r
102 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
103 \r
104 typedef struct {\r
105   POINT sq[2];    /* board coordinates of from, to squares */\r
106 } HighlightInfo;\r
107 \r
108 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
109 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
110 \r
111 /* Window class names */\r
112 char szAppName[] = "WinBoard";\r
113 char szConsoleName[] = "WBConsole";\r
114 \r
115 /* Title bar text */\r
116 char szTitle[] = "WinBoard";\r
117 char szConsoleTitle[] = "ICS Interaction";\r
118 \r
119 char *programName;\r
120 char *settingsFileName;\r
121 BOOLEAN saveSettingsOnExit;\r
122 char installDir[MSG_SIZ];\r
123 \r
124 BoardSize boardSize;\r
125 BOOLEAN chessProgram;\r
126 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
127 static int squareSize, lineGap;\r
128 static int winWidth, winHeight;\r
129 static RECT messageRect, whiteRect, blackRect;\r
130 static char messageText[MESSAGE_TEXT_MAX];\r
131 static int clockTimerEvent = 0;\r
132 static int loadGameTimerEvent = 0;\r
133 static int analysisTimerEvent = 0;\r
134 static DelayedEventCallback delayedTimerCallback;\r
135 static int delayedTimerEvent = 0;\r
136 static int buttonCount = 2;\r
137 char *icsTextMenuString;\r
138 char *icsNames;\r
139 char *firstChessProgramNames;\r
140 char *secondChessProgramNames;\r
141 \r
142 #define ARG_MAX 20000\r
143 \r
144 #define PALETTESIZE 256\r
145 \r
146 HINSTANCE hInst;          /* current instance */\r
147 HWND hwndMain = NULL;        /* root window*/\r
148 HWND hwndConsole = NULL;\r
149 BOOLEAN alwaysOnTop = FALSE;\r
150 RECT boardRect;\r
151 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
152   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
153 HPALETTE hPal;\r
154 ColorClass currentColorClass;\r
155 \r
156 HWND hCommPort = NULL;    /* currently open comm port */\r
157 static HWND hwndPause;    /* pause button */\r
158 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];\r
159 static HBRUSH lightSquareBrush, darkSquareBrush,\r
160   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
161 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
162 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
163 static HPEN gridPen = NULL;\r
164 static HPEN highlightPen = NULL;\r
165 static HPEN premovePen = NULL;\r
166 static NPLOGPALETTE pLogPal;\r
167 static BOOL paletteChanged = FALSE;\r
168 static HICON iconWhite, iconBlack, iconCurrent;\r
169 static int doingSizing = FALSE;\r
170 static int lastSizing = 0;\r
171 \r
172 #if __GNUC__ && !defined(_winmajor)\r
173 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
174 #else\r
175 #define oldDialog (_winmajor < 4)\r
176 #endif\r
177 \r
178 char *defaultTextAttribs[] = \r
179 {\r
180   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
181   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
182   COLOR_NONE\r
183 };\r
184 \r
185 typedef struct {\r
186   char *name;\r
187   int squareSize;\r
188   int lineGap;\r
189   int smallLayout;\r
190   int tinyLayout;\r
191   int cliWidth, cliHeight;\r
192 } SizeInfo;\r
193 \r
194 SizeInfo sizeInfo[] = \r
195 {\r
196   { "tiny",     21, 0, 1, 1, 0, 0 },\r
197   { "teeny",    25, 1, 1, 1, 0, 0 },\r
198   { "dinky",    29, 1, 1, 1, 0, 0 },\r
199   { "petite",   33, 1, 1, 1, 0, 0 },\r
200   { "slim",     37, 2, 1, 0, 0, 0 },\r
201   { "small",    40, 2, 1, 0, 0, 0 },\r
202   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
203   { "middling", 49, 2, 0, 0, 0, 0 },\r
204   { "average",  54, 2, 0, 0, 0, 0 },\r
205   { "moderate", 58, 3, 0, 0, 0, 0 },\r
206   { "medium",   64, 3, 0, 0, 0, 0 },\r
207   { "bulky",    72, 3, 0, 0, 0, 0 },\r
208   { "large",    80, 3, 0, 0, 0, 0 },\r
209   { "big",      87, 3, 0, 0, 0, 0 },\r
210   { "huge",     95, 3, 0, 0, 0, 0 },\r
211   { "giant",    108, 3, 0, 0, 0, 0 },\r
212   { "colossal", 116, 4, 0, 0, 0, 0 },\r
213   { "titanic",  129, 4, 0, 0, 0, 0 },\r
214   { NULL, 0, 0, 0, 0, 0, 0 }\r
215 };\r
216 \r
217 #define MF(x) {x, {0, }, {0, }, 0}\r
218 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
219 {\r
220   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), \r
221     MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),\r
222     MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },\r
223   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), \r
224     MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),\r
225     MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },\r
226   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),\r
227     MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),\r
228     MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },\r
229   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),\r
230     MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),\r
231     MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },\r
232   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),\r
233     MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),\r
234     MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },\r
235   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),\r
236     MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),\r
237     MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },\r
238   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),\r
239     MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),\r
240     MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },\r
241   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),\r
242     MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),\r
243     MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },\r
244   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),\r
245     MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),\r
246     MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },\r
247   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),\r
248     MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),\r
249     MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },\r
250   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),\r
251     MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),\r
252     MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },\r
253   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),\r
254     MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),\r
255     MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },\r
256   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),\r
257     MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),\r
258     MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },\r
259   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),\r
260     MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),\r
261     MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },\r
262   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),\r
263     MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),\r
264     MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },\r
265   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),\r
266     MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),\r
267     MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },\r
268   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),\r
269     MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),\r
270     MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },\r
271   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),\r
272     MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),\r
273     MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },\r
274 };\r
275 \r
276 MyFont *font[NUM_SIZES][NUM_FONTS];\r
277 \r
278 typedef struct {\r
279   char *label;\r
280   int id;\r
281   HWND hwnd;\r
282   WNDPROC wndproc;\r
283 } MyButtonDesc;\r
284 \r
285 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
286 #define N_BUTTONS 5\r
287 \r
288 MyButtonDesc buttonDesc[N_BUTTONS] =\r
289 {\r
290   {"<<", IDM_ToStart, NULL, NULL},\r
291   {"<", IDM_Backward, NULL, NULL},\r
292   {"P", IDM_Pause, NULL, NULL},\r
293   {">", IDM_Forward, NULL, NULL},\r
294   {">>", IDM_ToEnd, NULL, NULL},\r
295 };\r
296 \r
297 int tinyLayout = 0, smallLayout = 0;\r
298 #define MENU_BAR_ITEMS 6\r
299 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
300   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
301   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
302 };\r
303 \r
304 \r
305 MySound sounds[(int)NSoundClasses];\r
306 MyTextAttribs textAttribs[(int)NColorClasses];\r
307 \r
308 MyColorizeAttribs colorizeAttribs[] = {\r
309   { (COLORREF)0, 0, "Shout Text" },\r
310   { (COLORREF)0, 0, "SShout/CShout" },\r
311   { (COLORREF)0, 0, "Channel 1 Text" },\r
312   { (COLORREF)0, 0, "Channel Text" },\r
313   { (COLORREF)0, 0, "Kibitz Text" },\r
314   { (COLORREF)0, 0, "Tell Text" },\r
315   { (COLORREF)0, 0, "Challenge Text" },\r
316   { (COLORREF)0, 0, "Request Text" },\r
317   { (COLORREF)0, 0, "Seek Text" },\r
318   { (COLORREF)0, 0, "Normal Text" },\r
319   { (COLORREF)0, 0, "None" }\r
320 };\r
321 \r
322 \r
323 \r
324 static char *commentTitle;\r
325 static char *commentText;\r
326 static int commentIndex;\r
327 static Boolean editComment = FALSE;\r
328 HWND commentDialog = NULL;\r
329 BOOLEAN commentDialogUp = FALSE;\r
330 static int commentX, commentY, commentH, commentW;\r
331 \r
332 static char *analysisTitle;\r
333 static char *analysisText;\r
334 HWND analysisDialog = NULL;\r
335 BOOLEAN analysisDialogUp = FALSE;\r
336 static int analysisX, analysisY, analysisH, analysisW;\r
337 \r
338 char errorMessage[2*MSG_SIZ];\r
339 HWND errorDialog = NULL;\r
340 BOOLEAN moveErrorMessageUp = FALSE;\r
341 BOOLEAN consoleEcho = TRUE;\r
342 CHARFORMAT consoleCF;\r
343 COLORREF consoleBackgroundColor;\r
344 \r
345 char *programVersion;\r
346 \r
347 #include <winsock.h>\r
348 \r
349 #define CPReal 1\r
350 #define CPComm 2\r
351 #define CPSock 3\r
352 #define CPRcmd 4\r
353 typedef int CPKind;\r
354 \r
355 typedef struct {\r
356   CPKind kind;\r
357   HANDLE hProcess;\r
358   DWORD pid;\r
359   HANDLE hTo;\r
360   HANDLE hFrom;\r
361   SOCKET sock;\r
362   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
363 } ChildProc;\r
364 \r
365 #define INPUT_SOURCE_BUF_SIZE 4096\r
366 \r
367 typedef struct _InputSource {\r
368   CPKind kind;\r
369   HANDLE hFile;\r
370   SOCKET sock;\r
371   int lineByLine;\r
372   HANDLE hThread;\r
373   DWORD id;\r
374   char buf[INPUT_SOURCE_BUF_SIZE];\r
375   char *next;\r
376   DWORD count;\r
377   int error;\r
378   InputCallback func;\r
379   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
380   VOIDSTAR closure;\r
381 } InputSource;\r
382 \r
383 InputSource *consoleInputSource;\r
384 \r
385 DCB dcb;\r
386 \r
387 /* forward */\r
388 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
389 VOID ConsoleCreate();\r
390 LRESULT CALLBACK\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
395 LRESULT CALLBACK\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
401 \r
402 /*\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
405  */\r
406 static int frozen = 0;\r
407 static int oldMenuItemState[MENU_BAR_ITEMS];\r
408 void FreezeUI()\r
409 {\r
410   HMENU hmenu;\r
411   int i;\r
412 \r
413   if (frozen) return;\r
414   frozen = 1;\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
418   }\r
419   DrawMenuBar(hwndMain);\r
420 }\r
421 \r
422 /* Undo a FreezeUI */\r
423 void ThawUI()\r
424 {\r
425   HMENU hmenu;\r
426   int i;\r
427 \r
428   if (!frozen) return;\r
429   frozen = 0;\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
433   }\r
434   DrawMenuBar(hwndMain);\r
435 }\r
436 \r
437 /*---------------------------------------------------------------------------*\\r
438  *\r
439  * WinMain\r
440  *\r
441 \*---------------------------------------------------------------------------*/\r
442 \r
443 int APIENTRY\r
444 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
445         LPSTR lpCmdLine, int nCmdShow)\r
446 {\r
447   MSG msg;\r
448   HANDLE hAccelMain, hAccelNoAlt;\r
449 \r
450   debugFP = stderr;\r
451 \r
452   LoadLibrary("RICHED32.DLL");\r
453   consoleCF.cbSize = sizeof(CHARFORMAT);\r
454 \r
455   if (!InitApplication(hInstance)) {\r
456     return (FALSE);\r
457   }\r
458   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
459     return (FALSE);\r
460   }\r
461 \r
462   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
463   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
464 \r
465   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
466 \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
471     {\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
480       }\r
481     }\r
482 \r
483 \r
484   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
485 }\r
486 \r
487 /*---------------------------------------------------------------------------*\\r
488  *\r
489  * Initialization functions\r
490  *\r
491 \*---------------------------------------------------------------------------*/\r
492 \r
493 BOOL\r
494 InitApplication(HINSTANCE hInstance)\r
495 {\r
496   WNDCLASS wc;\r
497 \r
498   /* Fill in window class structure with parameters that describe the */\r
499   /* main window. */\r
500 \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
511 \r
512   /* Register the window class and return success/failure code. */\r
513   if (!RegisterClass(&wc)) return FALSE;\r
514 \r
515   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
516   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
517   wc.cbClsExtra    = 0;\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
525 \r
526   if (!RegisterClass(&wc)) return FALSE;\r
527   return TRUE;\r
528 }\r
529 \r
530 \r
531 /* Set by InitInstance, used by EnsureOnScreen */\r
532 int screenHeight, screenWidth;\r
533 \r
534 void\r
535 EnsureOnScreen(int *x, int *y)\r
536 {\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
540 }\r
541 \r
542 BOOL\r
543 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
544 {\r
545   HWND hwnd; /* Main window handle. */\r
546   int ibs;\r
547   WINDOWPLACEMENT wp;\r
548   char *filepart;\r
549 \r
550   hInst = hInstance;    /* Store instance handle in our global variable */\r
551 \r
552   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
553     *filepart = NULLCHAR;\r
554   } else {\r
555     GetCurrentDirectory(MSG_SIZ, installDir);\r
556   }\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
561   }\r
562 \r
563   InitBackEnd1();\r
564 \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
570   hwndMain = hwnd;\r
571 \r
572   /* If window could not be created, return "failure" */\r
573   if (!hwnd) {\r
574     return (FALSE);\r
575   }\r
576 \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
590     }\r
591   }\r
592   InitDrawingSizes(boardSize, 0);\r
593   InitMenuChecks();\r
594   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
595 \r
596   /* Make a console window if needed */\r
597   if (appData.icsActive) {\r
598     ConsoleCreate();\r
599   }\r
600 \r
601   InitBackEnd2();\r
602 \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
606   wp.flags = 0;\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
614 \r
615   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
616                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
617   if (hwndConsole) {\r
618 #if AOT_CONSOLE\r
619     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
620                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
621 #endif\r
622     ShowWindow(hwndConsole, nCmdShow);\r
623   }\r
624   UpdateWindow(hwnd);\r
625 \r
626   return TRUE;\r
627 \r
628 }\r
629 \r
630 \r
631 typedef enum {\r
632   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
633   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
634   ArgSettingsFilename\r
635 } ArgType;\r
636 \r
637 typedef struct {\r
638   char *argName;\r
639   ArgType argType;\r
640   /***\r
641   union {\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
653   } argLoc;\r
654   ***/\r
655   LPVOID argLoc;\r
656   BOOL save;\r
657 } ArgDescriptor;\r
658 \r
659 int junk;\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
683     FALSE },\r
684   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
685     FALSE },\r
686   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
687     FALSE },\r
688   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
689   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
690     FALSE },\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
843 #if 0\r
844   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
845   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
846 #endif\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
1001     TRUE },\r
1002   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1003     TRUE },\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
1008     FALSE },\r
1009   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,\r
1010     FALSE },\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
1015 #ifdef ZIPPY\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
1029     FALSE },\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
1037     FALSE },\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
1056 #endif\r
1057   { NULL, ArgNone, NULL, FALSE }\r
1058 };\r
1059 \r
1060 \r
1061 /* Kludge for indirection files on command line */\r
1062 char* lastIndirectionFilename;\r
1063 ArgDescriptor argDescriptorIndirection =\r
1064 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1065 \r
1066 \r
1067 VOID\r
1068 ExitArgError(char *msg, char *badArg)\r
1069 {\r
1070   char buf[MSG_SIZ];\r
1071 \r
1072   sprintf(buf, "%s %s", msg, badArg);\r
1073   DisplayFatalError(buf, 0, 2);\r
1074   exit(2);\r
1075 }\r
1076 \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
1081 */\r
1082 VOID\r
1083 ParseFontName(char *name, MyFontParams *mfp)\r
1084 {\r
1085   char *p, *q;\r
1086   if (name == NULL) return;\r
1087   p = name;\r
1088   q = strchr(p, ':');\r
1089   if (q) {\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
1094     p = q + 1;\r
1095   } else {\r
1096     q = mfp->faceName;\r
1097     while (*p && !isdigit(*p)) {\r
1098       *q++ = *p++;\r
1099       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1100         ExitArgError("Font name too long:", name);\r
1101     }\r
1102     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1103     *q = NULLCHAR;\r
1104   }\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
1111 }\r
1112 \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
1116 COLORREF\r
1117 ParseColorName(char *name)\r
1118 {\r
1119   int red, green, blue, count;\r
1120   char buf[MSG_SIZ];\r
1121 \r
1122   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1123   if (count != 3) {\r
1124     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1125       &red, &green, &blue);\r
1126   }\r
1127   if (count != 3) {\r
1128     sprintf(buf, "Can't parse color name %s", name);\r
1129     DisplayError(buf, 0);\r
1130     return RGB(0, 0, 0);\r
1131   }\r
1132   return PALETTERGB(red, green, blue);\r
1133 }\r
1134 \r
1135 \r
1136 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1137 {\r
1138   char *e = argValue;\r
1139   int eff = 0;\r
1140 \r
1141   while (*e) {\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
1147     e++;\r
1148   }\r
1149   *effects = eff;\r
1150   *color   = ParseColorName(e);\r
1151 }\r
1152 \r
1153 \r
1154 BoardSize\r
1155 ParseBoardSize(char *name)\r
1156 {\r
1157   BoardSize bs = SizeTiny;\r
1158   while (sizeInfo[bs].name != NULL) {\r
1159     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1160     bs++;\r
1161   }\r
1162   ExitArgError("Unrecognized board size value", name);\r
1163   return bs; /* not reached */\r
1164 }\r
1165 \r
1166 \r
1167 char\r
1168 StringGet(void *getClosure)\r
1169 {\r
1170   char **p = (char **) getClosure;\r
1171   return *((*p)++);\r
1172 }\r
1173 \r
1174 char\r
1175 FileGet(void *getClosure)\r
1176 {\r
1177   int c;\r
1178   FILE* f = (FILE*) getClosure;\r
1179 \r
1180   c = getc(f);\r
1181   if (c == EOF)\r
1182     return NULLCHAR;\r
1183   else\r
1184     return (char) c;\r
1185 }\r
1186 \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
1189 BOOLEAN\r
1190 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1191 {\r
1192   char *dummy;\r
1193   FILE *f;\r
1194 \r
1195   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1196     f = fopen(fullname, "r");\r
1197     if (f != NULL) {\r
1198       ParseArgs(FileGet, f);\r
1199       fclose(f);\r
1200       return TRUE;\r
1201     }\r
1202   }\r
1203   return FALSE;\r
1204 }\r
1205 \r
1206 VOID\r
1207 ParseArgs(GetFunc get, void *cl)\r
1208 {\r
1209   char argName[ARG_MAX];\r
1210   char argValue[ARG_MAX];\r
1211   ArgDescriptor *ad;\r
1212   char start;\r
1213   char *q;\r
1214   int i, octval;\r
1215   char ch;\r
1216   int posarg = 0;\r
1217 \r
1218   ch = get(cl);\r
1219   for (;;) {\r
1220     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1221     if (ch == NULLCHAR) break;\r
1222     if (ch == ';') {\r
1223       /* Comment to end of line */\r
1224       ch = get(cl);\r
1225       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1226       continue;\r
1227     } else if (ch == '/' || ch == '-') {\r
1228       /* Switch */\r
1229       q = argName;\r
1230       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1231              ch != '\n' && ch != '\t') {\r
1232         *q++ = ch;\r
1233         ch = get(cl);\r
1234       }\r
1235       *q = NULLCHAR;\r
1236 \r
1237       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1238         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1239 \r
1240       if (ad->argName == NULL)\r
1241         ExitArgError("Unrecognized argument", argName);\r
1242 \r
1243     } else if (ch == '@') {\r
1244       /* Indirection file */\r
1245       ad = &argDescriptorIndirection;\r
1246       ch = get(cl);\r
1247     } else {\r
1248       /* Positional argument */\r
1249       ad = &argDescriptors[posarg++];\r
1250       strcpy(argName, ad->argName);\r
1251     }\r
1252 \r
1253     if (ad->argType == ArgTrue) {\r
1254       *(Boolean *) ad->argLoc = TRUE;\r
1255       continue;\r
1256     }\r
1257     if (ad->argType == ArgFalse) {\r
1258       *(Boolean *) ad->argLoc = FALSE;\r
1259       continue;\r
1260     }\r
1261 \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
1265     }\r
1266     q = argValue;\r
1267     if (ch == '{') {\r
1268       // Quoting with { }.  No characters have to (or can) be escaped.\r
1269       // Thus the string cannot contain a '}' character.\r
1270       start = ch;\r
1271       ch = get(cl);\r
1272       while (start) {\r
1273         switch (ch) {\r
1274         case NULLCHAR:\r
1275           start = NULLCHAR;\r
1276           break;\r
1277           \r
1278         case '}':\r
1279           ch = get(cl);\r
1280           start = NULLCHAR;\r
1281           break;\r
1282 \r
1283         default:\r
1284           *q++ = ch;\r
1285           ch = get(cl);\r
1286           break;\r
1287         }\r
1288       }   \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
1292       start = ch;\r
1293       ch = get(cl);\r
1294       while (start) {\r
1295         switch (ch) {\r
1296         case NULLCHAR:\r
1297           start = NULLCHAR;\r
1298           break;\r
1299 \r
1300         default:\r
1301         not_special:\r
1302           *q++ = ch;\r
1303           ch = get(cl);\r
1304           break;\r
1305 \r
1306         case '\'':\r
1307         case '\"':\r
1308           if (ch == start) {\r
1309             ch = get(cl);\r
1310             start = NULLCHAR;\r
1311             break;\r
1312           } else {\r
1313             goto not_special;\r
1314           }\r
1315 \r
1316         case '\\':\r
1317           if (ad->argType == ArgFilename\r
1318               || ad->argType == ArgSettingsFilename) {\r
1319               goto not_special;\r
1320           }\r
1321           ch = get(cl);\r
1322           switch (ch) {\r
1323           case NULLCHAR:\r
1324             ExitArgError("Incomplete \\ escape in value for", argName);\r
1325             break;\r
1326           case 'n':\r
1327             *q++ = '\n';\r
1328             ch = get(cl);\r
1329             break;\r
1330           case 'r':\r
1331             *q++ = '\r';\r
1332             ch = get(cl);\r
1333             break;\r
1334           case 't':\r
1335             *q++ = '\t';\r
1336             ch = get(cl);\r
1337             break;\r
1338           case 'b':\r
1339             *q++ = '\b';\r
1340             ch = get(cl);\r
1341             break;\r
1342           case 'f':\r
1343             *q++ = '\f';\r
1344             ch = get(cl);\r
1345             break;\r
1346           default:\r
1347             octval = 0;\r
1348             for (i = 0; i < 3; i++) {\r
1349               if (ch >= '0' && ch <= '7') {\r
1350                 octval = octval*8 + (ch - '0');\r
1351                 ch = get(cl);\r
1352               } else {\r
1353                 break;\r
1354               }\r
1355             }\r
1356             if (i > 0) {\r
1357               *q++ = (char) octval;\r
1358             } else {\r
1359               *q++ = ch;\r
1360               ch = get(cl);\r
1361             }\r
1362             break;\r
1363           }\r
1364           break;\r
1365         }\r
1366       }\r
1367     } else {\r
1368       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1369         *q++ = ch;\r
1370         ch = get(cl);\r
1371       }\r
1372     }\r
1373     *q = NULLCHAR;\r
1374 \r
1375     switch (ad->argType) {\r
1376     case ArgInt:\r
1377       *(int *) ad->argLoc = atoi(argValue);\r
1378       break;\r
1379 \r
1380     case ArgFloat:\r
1381       *(float *) ad->argLoc = (float) atof(argValue);\r
1382       break;\r
1383 \r
1384     case ArgString:\r
1385     case ArgFilename:\r
1386       *(char **) ad->argLoc = strdup(argValue);\r
1387       break;\r
1388 \r
1389     case ArgSettingsFilename:\r
1390       {\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
1395           }\r
1396         } else {\r
1397           if (ad->argLoc != NULL) {\r
1398           } else {\r
1399             ExitArgError("Failed to open indirection file", argValue);\r
1400           }\r
1401         }\r
1402       }\r
1403       break;\r
1404 \r
1405     case ArgBoolean:\r
1406       switch (argValue[0]) {\r
1407       case 't':\r
1408       case 'T':\r
1409         *(Boolean *) ad->argLoc = TRUE;\r
1410         break;\r
1411       case 'f':\r
1412       case 'F':\r
1413         *(Boolean *) ad->argLoc = FALSE;\r
1414         break;\r
1415       default:\r
1416         ExitArgError("Unrecognized boolean argument value", argValue);\r
1417         break;\r
1418       }\r
1419       break;\r
1420 \r
1421     case ArgColor:\r
1422       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1423       break;\r
1424 \r
1425     case ArgAttribs: {\r
1426       ColorClass cc = (ColorClass)ad->argLoc;\r
1427       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1428       }\r
1429       break;\r
1430       \r
1431     case ArgBoardSize:\r
1432       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1433       break;\r
1434 \r
1435     case ArgFont:\r
1436       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1437       break;\r
1438 \r
1439     case ArgCommSettings:\r
1440       ParseCommSettings(argValue, &dcb);\r
1441       break;\r
1442 \r
1443     case ArgNone:\r
1444       ExitArgError("Unrecognized argument", argValue);\r
1445       break;\r
1446     }\r
1447   }\r
1448 }\r
1449 \r
1450 VOID\r
1451 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1452 {\r
1453   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1454   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1455   DeleteDC(hdc);\r
1456   lf->lfWidth = 0;\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
1469 }\r
1470 \r
1471 VOID\r
1472 CreateFontInMF(MyFont *mf)\r
1473 {\r
1474   LFfromMFP(&mf->lf, &mf->mfp);\r
1475   if (mf->hf) DeleteObject(mf->hf);\r
1476   mf->hf = CreateFontIndirect(&mf->lf);\r
1477 }\r
1478 \r
1479 VOID\r
1480 SetDefaultTextAttribs()\r
1481 {\r
1482   ColorClass cc;\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
1487   }\r
1488 }\r
1489 \r
1490 VOID\r
1491 SetDefaultSounds()\r
1492 {\r
1493   ColorClass cc;\r
1494   SoundClass sc;\r
1495   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1496     textAttribs[cc].sound.name = strdup("");\r
1497     textAttribs[cc].sound.data = NULL;\r
1498   }\r
1499   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1500     sounds[sc].name = strdup("");\r
1501     sounds[sc].data = NULL;\r
1502   }\r
1503   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1504 }\r
1505 \r
1506 VOID\r
1507 LoadAllSounds()\r
1508 {\r
1509   ColorClass cc;\r
1510   SoundClass sc;\r
1511   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1512     MyLoadSound(&textAttribs[cc].sound);\r
1513   }\r
1514   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1515     MyLoadSound(&sounds[sc]);\r
1516   }\r
1517 }\r
1518 \r
1519 VOID\r
1520 InitAppData(LPSTR lpCmdLine)\r
1521 {\r
1522   int i, j;\r
1523   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1524   char *dummy, *p;\r
1525 \r
1526   programName = szAppName;\r
1527 \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
1628   dcb.fInX = FALSE;\r
1629   dcb.fNull = FALSE;\r
1630   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1631   dcb.fAbortOnError = FALSE;\r
1632   dcb.wReserved = 0;\r
1633   dcb.ByteSize = 7;\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
1669 #ifdef ZIPPY\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
1688 #endif\r
1689 \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
1696     }\r
1697   }\r
1698   \r
1699   /* Parse default settings file if any */\r
1700   if (ParseSettingsFile(settingsFileName, buf)) {\r
1701     settingsFileName = strdup(buf);\r
1702   }\r
1703 \r
1704   /* Parse command line */\r
1705   ParseArgs(StringGet, &lpCmdLine);\r
1706 \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
1711   }\r
1712 \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
1718     FARPROC lpProc;\r
1719     \r
1720     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1721     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1722     FreeProcInstance(lpProc);\r
1723   }\r
1724 \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
1728   }\r
1729   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1730     appData.savePositionFile = strdup(buf);\r
1731   }\r
1732 \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
1737     }\r
1738   }\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
1749   }\r
1750   GetCurrentDirectory(MSG_SIZ, currDir);\r
1751   SetCurrentDirectory(installDir);\r
1752   LoadAllSounds();\r
1753   SetCurrentDirectory(currDir);\r
1754 \r
1755   p = icsTextMenuString;\r
1756   if (p[0] == '@') {\r
1757     FILE* f = fopen(p + 1, "r");\r
1758     if (f == NULL) {\r
1759       DisplayFatalError(p + 1, errno, 2);\r
1760       return;\r
1761     }\r
1762     i = fread(buf, 1, sizeof(buf)-1, f);\r
1763     fclose(f);\r
1764     buf[i] = NULLCHAR;\r
1765     p = buf;\r
1766   }\r
1767   ParseIcsTextMenu(strdup(p));\r
1768 }\r
1769 \r
1770 \r
1771 VOID\r
1772 InitMenuChecks()\r
1773 {\r
1774   HMENU hmenu = GetMenu(hwndMain);\r
1775 \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
1783 }\r
1784 \r
1785 \r
1786 VOID\r
1787 SaveSettings(char* name)\r
1788 {\r
1789   FILE *f;\r
1790   ArgDescriptor *ad;\r
1791   WINDOWPLACEMENT wp;\r
1792   char dir[MSG_SIZ];\r
1793 \r
1794   if (!hwndMain) return;\r
1795 \r
1796   GetCurrentDirectory(MSG_SIZ, dir);\r
1797   SetCurrentDirectory(installDir);\r
1798   f = fopen(name, "w");\r
1799   SetCurrentDirectory(dir);\r
1800   if (f == NULL) {\r
1801     DisplayError(name, errno);\r
1802     return;\r
1803   }\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
1811 \r
1812   wp.length = sizeof(WINDOWPLACEMENT);\r
1813   GetWindowPlacement(hwndMain, &wp);\r
1814   boardX = wp.rcNormalPosition.left;\r
1815   boardY = wp.rcNormalPosition.top;\r
1816 \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
1823   }\r
1824 \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
1831   }\r
1832 \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
1839   }\r
1840 \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
1847   }\r
1848 \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
1855   }\r
1856 \r
1857   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
1858     if (!ad->save) continue;\r
1859     switch (ad->argType) {\r
1860     case ArgString:\r
1861       {\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
1867         } else {\r
1868           /* Else quote with " " */\r
1869           fprintf(f, "/%s=\"", ad->argName);\r
1870           while (*p) {\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
1879             else putc(*p, f);\r
1880             p++;\r
1881           }\r
1882           fprintf(f, "\"\n");\r
1883         }\r
1884       }\r
1885       break;\r
1886     case ArgInt:\r
1887       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
1888       break;\r
1889     case ArgFloat:\r
1890       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
1891       break;\r
1892     case ArgBoolean:\r
1893       fprintf(f, "/%s=%s\n", ad->argName, \r
1894         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
1895       break;\r
1896     case ArgTrue:\r
1897       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
1898       break;\r
1899     case ArgFalse:\r
1900       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
1901       break;\r
1902     case ArgColor:\r
1903       {\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
1907       }\r
1908       break;\r
1909     case ArgAttribs:\r
1910       {\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
1919       }\r
1920       break;\r
1921     case ArgFilename:\r
1922       if (strchr(*(char **)ad->argLoc, '\"')) {\r
1923         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
1924       } else {\r
1925         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
1926       }\r
1927       break;\r
1928     case ArgBoardSize:\r
1929       fprintf(f, "/%s=%s\n", ad->argName,\r
1930               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
1931       break;\r
1932     case ArgFont:\r
1933       {\r
1934         int bs;\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
1945         }\r
1946       }\r
1947       break;\r
1948     case ArgCommSettings:\r
1949       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
1950     }\r
1951   }\r
1952   fclose(f);\r
1953 }\r
1954 \r
1955 \r
1956 \r
1957 /*---------------------------------------------------------------------------*\\r
1958  *\r
1959  * GDI board drawing routines\r
1960  *\r
1961 \*---------------------------------------------------------------------------*/\r
1962 \r
1963 HBITMAP\r
1964 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
1965 {\r
1966   char name[128];\r
1967 \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
1973   }\r
1974   return LoadBitmap(hinst, name);\r
1975 }\r
1976 \r
1977 \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
1982 */\r
1983 VOID\r
1984 InsertInPalette(COLORREF color)\r
1985 {\r
1986   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
1987 \r
1988   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
1989     DisplayFatalError("Too many colors", 0, 1);\r
1990     pLogPal->palNumEntries--;\r
1991     return;\r
1992   }\r
1993 \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
1998   return;\r
1999 }\r
2000 \r
2001 \r
2002 VOID\r
2003 InitDrawingColors()\r
2004 {\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
2009      */\r
2010     pLogPal = (NPLOGPALETTE)\r
2011       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2012                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2013     pLogPal->palVersion    = 0x300;\r
2014   }\r
2015   pLogPal->palNumEntries = 0;\r
2016 \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
2023 \r
2024   /*  create a logical color palette according the information\r
2025    *  in the LOGPALETTE structure.\r
2026    */\r
2027   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2028 \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
2034 }\r
2035 \r
2036 \r
2037 int\r
2038 BoardWidth(int boardSize)\r
2039 {\r
2040   return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +\r
2041           BOARD_SIZE * sizeInfo[boardSize].squareSize;\r
2042 }\r
2043 \r
2044 /* Respond to board resize by dragging edge */\r
2045 VOID\r
2046 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2047 {\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
2052   recurse++;\r
2053   while (newSize > 0 &&\r
2054          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2055           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2056     newSize--;\r
2057   } \r
2058   boardSize = newSize;\r
2059   InitDrawingSizes(boardSize, flags);\r
2060   recurse--;\r
2061 }\r
2062 \r
2063 \r
2064 \r
2065 VOID\r
2066 InitDrawingSizes(BoardSize boardSize, int flags)\r
2067 {\r
2068   int i, boardWidth;\r
2069   ChessSquare piece;\r
2070   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2071   HDC hdc;\r
2072   SIZE clockSize, messageSize;\r
2073   HFONT oldFont;\r
2074   char buf[MSG_SIZ];\r
2075   char *str;\r
2076   HMENU hmenu = GetMenu(hwndMain);\r
2077   RECT crect, wrect;\r
2078   int offby;\r
2079   LOGBRUSH logbrush;\r
2080 \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
2085 \r
2086   if (tinyLayout != oldTinyLayout) {\r
2087     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2088     if (tinyLayout) {\r
2089       style &= ~WS_SYSMENU;\r
2090       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2091                  "&Minimize\tCtrl+F4");\r
2092     } else {\r
2093       style |= WS_SYSMENU;\r
2094       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2095     }\r
2096     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2097 \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
2101     }\r
2102     DrawMenuBar(hwndMain);\r
2103   }\r
2104 \r
2105   boardWidth = BoardWidth(boardSize);\r
2106 \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
2111   } else {\r
2112     sprintf(buf, "White");\r
2113   }\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
2121 \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
2127 \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
2132 \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
2137   } else {\r
2138     messageRect.right = blackRect.right;\r
2139   }\r
2140   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2141   messageRect.bottom = messageRect.top + messageSize.cy;\r
2142 \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
2147 \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
2160   switch (flags) {\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
2165     break;\r
2166 \r
2167   case WMSZ_TOPRIGHT:\r
2168   case WMSZ_TOP:\r
2169     SetWindowPos(hwndMain, NULL, \r
2170                  wrect.left, wrect.bottom - winHeight, \r
2171                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2172     break;\r
2173 \r
2174   case WMSZ_BOTTOMLEFT:\r
2175   case WMSZ_LEFT:\r
2176     SetWindowPos(hwndMain, NULL, \r
2177                  wrect.right - winWidth, wrect.top, \r
2178                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2179     break;\r
2180 \r
2181   case WMSZ_BOTTOMRIGHT:\r
2182   case WMSZ_BOTTOM:\r
2183   case WMSZ_RIGHT:\r
2184   default:\r
2185     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2186                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2187     break;\r
2188   }\r
2189 \r
2190   hwndPause = NULL;\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
2195     }\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
2204       if (tinyLayout) {\r
2205         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2206                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2207                     MAKELPARAM(FALSE, 0));\r
2208       }\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
2213     }\r
2214   }\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
2221     gridPen =\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
2225     highlightPen =\r
2226       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2227                    lineGap, &logbrush, 0, NULL);\r
2228 \r
2229     logbrush.lbColor = premoveHighlightColor; \r
2230     premovePen =\r
2231       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2232                    lineGap, &logbrush, 0, NULL);\r
2233 \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
2247     }\r
2248   }\r
2249 \r
2250   if (boardSize == oldBoardSize) return;\r
2251   oldBoardSize = boardSize;\r
2252   oldTinyLayout = tinyLayout;\r
2253 \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
2261     }\r
2262   }\r
2263 \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
2282 \r
2283 }\r
2284 \r
2285 HBITMAP\r
2286 PieceBitmap(ChessSquare p, int kind)\r
2287 {\r
2288   if ((int) p >= (int) BlackPawn)\r
2289     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2290 \r
2291   return pieceBitmap[kind][(int) p];\r
2292 }\r
2293 \r
2294 /***************************************************************/\r
2295 \r
2296 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2297 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2298 /*\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
2301 */\r
2302 \r
2303 VOID\r
2304 SquareToPos(int row, int column, int * x, int * y)\r
2305 {\r
2306   if (flipView) {\r
2307     *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
2308     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2309   } else {\r
2310     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2311     *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
2312   }\r
2313 }\r
2314 \r
2315 VOID\r
2316 DrawCoordsOnDC(HDC hdc)\r
2317 {\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
2322   HFONT oldFont;\r
2323   HBRUSH oldBrush;\r
2324 \r
2325   if (!appData.showCoords)\r
2326     return;\r
2327 \r
2328   start = flipView ? 0 : 8;\r
2329 \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
2334 \r
2335   y = boardRect.top + lineGap;\r
2336   x = boardRect.left + lineGap;\r
2337 \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
2343   }\r
2344 \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
2350   }    \r
2351 \r
2352   SelectObject(hdc, oldBrush);\r
2353   SetBkMode(hdc, oldMode);\r
2354   SetTextAlign(hdc, oldAlign);\r
2355   SelectObject(hdc, oldFont);\r
2356 }\r
2357 \r
2358 VOID\r
2359 DrawGridOnDC(HDC hdc)\r
2360 {\r
2361   HPEN oldPen;\r
2362  \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
2367   }\r
2368 }\r
2369 \r
2370 #define HIGHLIGHT_PEN 0\r
2371 #define PREMOVE_PEN   1\r
2372 \r
2373 VOID\r
2374 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2375 {\r
2376   int x1, y1;\r
2377   HPEN oldPen, hPen;\r
2378   if (lineGap == 0) return;\r
2379   if (flipView) {\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
2384   } else {\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
2389   }\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
2398 }\r
2399 \r
2400 VOID\r
2401 DrawHighlightsOnDC(HDC hdc)\r
2402 {\r
2403   int i;\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
2408                         HIGHLIGHT_PEN);\r
2409   }\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
2416                           PREMOVE_PEN);\r
2417     }\r
2418   }\r
2419 }\r
2420 \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
2424 VOID\r
2425 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2426 {\r
2427   HBITMAP oldBitmap;\r
2428   HBRUSH oldBrush;\r
2429 \r
2430   if (appData.blindfold) return;\r
2431 \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
2437   } else {\r
2438     if (color) {\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
2442 #if 0\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
2449 #else\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
2453 #endif\r
2454     } else {\r
2455 #if 0\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
2467 #else\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
2472 #endif\r
2473     }\r
2474     SelectObject(hdc, oldBrush);\r
2475     SelectObject(tmphdc, oldBitmap);\r
2476   }\r
2477 }\r
2478 \r
2479 VOID\r
2480 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2481 {\r
2482   int row, column, x, y, square_color, piece_color;\r
2483   ChessSquare piece;\r
2484   HBRUSH oldBrush;\r
2485 \r
2486   for (row = 0; row < BOARD_SIZE; row++) {\r
2487     for (column = 0; column < BOARD_SIZE; column++) {\r
2488   \r
2489       SquareToPos(row, column, &x, &y);\r
2490 \r
2491       piece = board[row][column];\r
2492 \r
2493       square_color = ((column + row) % 2) == 1;\r
2494       piece_color = (int) piece < (int) BlackPawn;\r
2495 \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
2500         } else {\r
2501           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
2502         }\r
2503       } else {\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
2510       }\r
2511     }\r
2512   }\r
2513 }\r
2514 \r
2515 #define MAX_CLIPS 200   /* more than enough */\r
2516 \r
2517 VOID\r
2518 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
2519 {\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
2525   HDC tmphdc;\r
2526   HDC hdcmem;\r
2527   HBITMAP bufferBitmap;\r
2528   HBITMAP oldBitmap;\r
2529   RECT Rect;\r
2530   HRGN clips[MAX_CLIPS];\r
2531   ChessSquare dragged_piece = EmptySquare;\r
2532 \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
2540    */\r
2541   Boolean fullrepaint = repaint;\r
2542 \r
2543   if (board == NULL) {\r
2544     if (!lastReqValid) {\r
2545       return;\r
2546     }\r
2547     board = lastReq;\r
2548   } else {\r
2549     CopyBoard(lastReq, board);\r
2550     lastReqValid = 1;\r
2551   }\r
2552 \r
2553   if (doingSizing) {\r
2554     return;\r
2555   }\r
2556 \r
2557   if (IsIconic(hwndMain)) {\r
2558     return;\r
2559   }\r
2560 \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
2566     }\r
2567     releaseDC = TRUE;\r
2568   } else {\r
2569     releaseDC = FALSE;\r
2570   }\r
2571 \r
2572 #if 0\r
2573   fprintf(debugFP, "*******************************\n"\r
2574                    "repaint = %s\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
2588     }\r
2589   }\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
2595     }\r
2596   }\r
2597   fprintf(debugFP, "\n");\r
2598   fflush(debugFP);\r
2599 #endif\r
2600 \r
2601   /* Create some work-DCs */\r
2602   hdcmem = CreateCompatibleDC(hdc);\r
2603   tmphdc = CreateCompatibleDC(hdc);\r
2604 \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
2608    */\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
2616         }\r
2617       }\r
2618     }\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
2629         }\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
2635         }\r
2636       }\r
2637     }\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
2648         }\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
2656         }\r
2657       }\r
2658     }\r
2659   } else {\r
2660     fullrepaint = TRUE;\r
2661   }\r
2662 \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
2667    */\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
2674   }\r
2675 \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
2681     }\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
2685     }\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
2690     }\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
2695     }\r
2696   }\r
2697 \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
2702   }\r
2703 \r
2704   /* Are we animating a move?  \r
2705    * If so, \r
2706    *   - remove the piece from the board (temporarely)\r
2707    *   - calculate the clipping region\r
2708    */\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
2725     }\r
2726   }\r
2727 \r
2728   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
2729   if (num_clips == 0)\r
2730     fullrepaint = TRUE;\r
2731 \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
2738     }\r
2739   }\r
2740 \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
2746 \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
2755   }   \r
2756   \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
2765   }\r
2766 \r
2767   /* Release the bufferBitmap by selecting in the old bitmap \r
2768    * and delete the memory DC\r
2769    */\r
2770   SelectObject(hdcmem, oldBitmap);\r
2771   DeleteDC(hdcmem);\r
2772 \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
2779     } \r
2780   }\r
2781 \r
2782   /* Copy the new bitmap onto the screen in one go.\r
2783    * This way we avoid any flickering\r
2784    */\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
2791 \r
2792   /* Massive cleanup */\r
2793   for (x = 0; x < num_clips; x++)\r
2794     DeleteObject(clips[x]);\r
2795 \r
2796   DeleteDC(tmphdc);\r
2797   DeleteObject(bufferBitmap);\r
2798 \r
2799   if (releaseDC) \r
2800     ReleaseDC(hwndMain, hdc);\r
2801   \r
2802   if (lastDrawnFlipView != flipView) {\r
2803     if (flipView)\r
2804       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
2805     else\r
2806       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
2807   }\r
2808 \r
2809   CopyBoard(lastDrawn, board);\r
2810   lastDrawnHighlight = highlightInfo;\r
2811   lastDrawnPremove   = premoveHighlightInfo;\r
2812   lastDrawnFlipView = flipView;\r
2813   lastDrawnValid = 1;\r
2814 }\r
2815 \r
2816 \r
2817 /*---------------------------------------------------------------------------*\\r
2818 | CLIENT PAINT PROCEDURE\r
2819 |   This is the main event-handler for the WM_PAINT message.\r
2820 |\r
2821 \*---------------------------------------------------------------------------*/\r
2822 VOID\r
2823 PaintProc(HWND hwnd)\r
2824 {\r
2825   HDC         hdc;\r
2826   PAINTSTRUCT ps;\r
2827   HFONT       oldFont;\r
2828 \r
2829   if(hdc = BeginPaint(hwnd, &ps)) {\r
2830     if (IsIconic(hwnd)) {\r
2831       DrawIcon(hdc, 2, 2, iconCurrent);\r
2832     } else {\r
2833       if (!appData.monoMode) {\r
2834         SelectPalette(hdc, hPal, FALSE);\r
2835         RealizePalette(hdc);\r
2836       }\r
2837       HDCDrawPosition(hdc, 1, NULL);\r
2838       oldFont =\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
2845     }\r
2846     EndPaint(hwnd,&ps);\r
2847   }\r
2848 \r
2849   return;\r
2850 }\r
2851 \r
2852 \r
2853 /*\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
2858  */\r
2859 int\r
2860 EventToSquare(int x)\r
2861 {\r
2862   if (x <= 0)\r
2863     return -2;\r
2864   if (x < lineGap)\r
2865     return -1;\r
2866   x -= lineGap;\r
2867   if ((x % (squareSize + lineGap)) >= squareSize)\r
2868     return -1;\r
2869   x /= (squareSize + lineGap);\r
2870   if (x >= BOARD_SIZE)\r
2871     return -2;\r
2872   return x;\r
2873 }\r
2874 \r
2875 typedef struct {\r
2876   char piece;\r
2877   int command;\r
2878   char* name;\r
2879 } DropEnable;\r
2880 \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
2887 };\r
2888 \r
2889 VOID\r
2890 SetupDropMenu(HMENU hmenu)\r
2891 {\r
2892   int i, count, enable;\r
2893   char *p;\r
2894   extern char white_holding[], black_holding[];\r
2895   char item[MSG_SIZ];\r
2896 \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
2900     count = 0;\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
2909   }\r
2910 }\r
2911 \r
2912 static int fromX = -1, fromY = -1, toX, toY;\r
2913 \r
2914 /* Event handler for mouse messages */\r
2915 VOID\r
2916 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
2917 {\r
2918   int x, y;\r
2919   POINT pt;\r
2920   static int recursive = 0;\r
2921   HMENU hmenu;\r
2922   BOOLEAN saveAnimate;\r
2923   static BOOLEAN sameAgain = FALSE;\r
2924 \r
2925   if (recursive) {\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
2929          */\r
2930       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
2931       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
2932     }\r
2933     return;\r
2934   }\r
2935   recursive++;\r
2936   \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
2943   }\r
2944   if (flipView && x >= 0) {\r
2945     x = BOARD_SIZE - 1 - x;\r
2946   }\r
2947 \r
2948   switch (message) {\r
2949   case WM_LBUTTONDOWN:\r
2950     ErrorPopDown();\r
2951     sameAgain = FALSE;\r
2952     if (y == -2) {\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
2959           CallFlagEvent();\r
2960         }\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
2966           CallFlagEvent();\r
2967         }\r
2968       }\r
2969       if (!appData.highlightLastMove) {\r
2970         ClearHighlights();\r
2971         DrawPosition(FALSE, NULL);\r
2972       }\r
2973       fromX = fromY = -1;\r
2974       dragInfo.start.x = dragInfo.start.y = -1;\r
2975       dragInfo.from = dragInfo.start;\r
2976       break;\r
2977     } else if (x < 0 || y < 0) {\r
2978       break;\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
2996         toX = x;\r
2997         toY = y;\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
3004             }\r
3005           } else {\r
3006             SetHighlights(fromX, fromY, toX, toY);\r
3007             DrawPosition(FALSE, NULL);\r
3008             PromotionPopup(hwnd);\r
3009           }\r
3010         } else {        /* not a promotion */\r
3011           if (appData.animate || appData.highlightLastMove) {\r
3012             SetHighlights(fromX, fromY, toX, toY);\r
3013           } else {\r
3014             ClearHighlights();\r
3015           }\r
3016           UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
3017           if (appData.animate && !appData.highlightLastMove) {\r
3018             ClearHighlights();\r
3019             DrawPosition(FALSE, NULL);\r
3020           }\r
3021         }\r
3022         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
3023         fromX = fromY = -1;\r
3024         break;\r
3025       }\r
3026       ClearHighlights();\r
3027       DrawPosition(FALSE, NULL);\r
3028     }\r
3029     /* First downclick, or restart on a square with same color piece */\r
3030     if (!frozen && OKToStartUserMove(x, y)) {\r
3031       fromX = x;\r
3032       fromY = 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
3038     } else {\r
3039       fromX = fromY = -1;\r
3040       dragInfo.start.x = dragInfo.start.y = -1;\r
3041       dragInfo.from = dragInfo.start;\r
3042     }\r
3043     break;\r
3044 \r
3045   case WM_LBUTTONUP:\r
3046     ReleaseCapture();\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
3051       if (sameAgain) {\r
3052         /* Clicked same square twice: abort click-click move */\r
3053         fromX = fromY = -1;\r
3054         gotPremove = 0;\r
3055         ClearPremoveHighlights();\r
3056       } else {\r
3057         /* First square clicked: start click-click move */\r
3058         SetHighlights(fromX, fromY, -1, -1);\r
3059       }\r
3060       DrawPosition(FALSE, NULL);\r
3061     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
3062       /* Errant click; ignore */\r
3063       break;\r
3064     } else {\r
3065       /* Finish drag move */\r
3066       dragInfo.from.x = dragInfo.from.y = -1;\r
3067       toX = x;\r
3068       toY = y;\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
3074         } else {\r
3075           DrawPosition(FALSE, NULL);\r
3076           PromotionPopup(hwnd);\r
3077         }\r
3078       } else {\r
3079         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
3080       }\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
3086       }\r
3087       if (appData.animate || appData.animateDragging ||\r
3088           appData.highlightDragging || gotPremove) {\r
3089         DrawPosition(FALSE, NULL);\r
3090       }\r
3091     }\r
3092     dragInfo.start.x = dragInfo.start.y = -1; \r
3093     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
3094     break;\r
3095 \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
3102       }\r
3103       if (appData.highlightDragging) {\r
3104         SetHighlights(fromX, fromY, x, y);\r
3105       }\r
3106       DrawPosition(FALSE, NULL);\r
3107       dragInfo.lastpos = dragInfo.pos;\r
3108     }\r
3109     break;\r
3110 \r
3111   case WM_MBUTTONDOWN:\r
3112   case WM_RBUTTONDOWN:\r
3113     ErrorPopDown();\r
3114     ReleaseCapture();\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
3122     }\r
3123     DrawPosition(TRUE, NULL);\r
3124 \r
3125     switch (gameMode) {\r
3126     case EditPosition:\r
3127     case IcsExamining:\r
3128       if (x < 0 || y < 0) break;\r
3129       fromX = x;\r
3130       fromY = y;\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
3135         else\r
3136           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3137       } else { /* message == WM_RBUTTONDOWN */\r
3138 #if 0\r
3139         if (buttonCount == 3) {\r
3140           if (wParam & MK_SHIFT) \r
3141             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3142           else\r
3143             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3144         } else {\r
3145           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3146         }\r
3147 #else\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
3152 #endif\r
3153       }\r
3154       break;\r
3155     case IcsPlayingWhite:\r
3156     case IcsPlayingBlack:\r
3157     case EditGame:\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
3164       fromX = x;\r
3165       fromY = y;\r
3166       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3167       SetupDropMenu(hmenu);\r
3168       MenuPopup(hwnd, pt, hmenu, -1);\r
3169       break;\r
3170     default:\r
3171       break;\r
3172     }\r
3173     break;\r
3174   }\r
3175 \r
3176   recursive--;\r
3177 }\r
3178 \r
3179 /* Preprocess messages for buttons in main window */\r
3180 LRESULT CALLBACK\r
3181 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3182 {\r
3183   int id = GetWindowLong(hwnd, GWL_ID);\r
3184   int i, dir;\r
3185 \r
3186   for (i=0; i<N_BUTTONS; i++) {\r
3187     if (buttonDesc[i].id == id) break;\r
3188   }\r
3189   if (i == N_BUTTONS) return 0;\r
3190   switch (message) {\r
3191   case WM_KEYDOWN:\r
3192     switch (wParam) {\r
3193     case VK_LEFT:\r
3194     case VK_RIGHT:\r
3195       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3196       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3197       return TRUE;\r
3198     }\r
3199     break;\r
3200   case WM_CHAR:\r
3201     switch (wParam) {\r
3202     case '\r':\r
3203       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3204       return TRUE;\r
3205     case '\t':\r
3206       if (appData.icsActive) {\r
3207         if (GetKeyState(VK_SHIFT) < 0) {\r
3208           /* shifted */\r
3209           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3210           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3211           SetFocus(h);\r
3212         } else {\r
3213           /* unshifted */\r
3214           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3215           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3216           SetFocus(h);\r
3217         }\r
3218         return TRUE;\r
3219       }\r
3220       break;\r
3221     default:\r
3222       if (appData.icsActive) {\r
3223         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3224         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3225         SetFocus(h);\r
3226         SendMessage(h, WM_CHAR, wParam, lParam);\r
3227         return TRUE;\r
3228       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3229         PopUpMoveDialog((char)wParam);\r
3230       }\r
3231       break;\r
3232     }\r
3233     break;\r
3234   }\r
3235   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
3236 }\r
3237 \r
3238 /* Process messages for Promotion dialog box */\r
3239 LRESULT CALLBACK\r
3240 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
3241 {\r
3242   char promoChar;\r
3243 \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                SW_SHOW : SW_HIDE);\r
3251     return TRUE;\r
3252 \r
3253   case WM_COMMAND: /* message: received a command */\r
3254     switch (LOWORD(wParam)) {\r
3255     case IDCANCEL:\r
3256       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3257       ClearHighlights();\r
3258       DrawPosition(FALSE, NULL);\r
3259       return TRUE;\r
3260     case PB_King:\r
3261       promoChar = 'k';\r
3262       break;\r
3263     case PB_Queen:\r
3264       promoChar = 'q';\r
3265       break;\r
3266     case PB_Rook:\r
3267       promoChar = 'r';\r
3268       break;\r
3269     case PB_Bishop:\r
3270       promoChar = 'b';\r
3271       break;\r
3272     case PB_Knight:\r
3273       promoChar = 'n';\r
3274       break;\r
3275     default:\r
3276       return FALSE;\r
3277     }\r
3278     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3279     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
3280     if (!appData.highlightLastMove) {\r
3281       ClearHighlights();\r
3282       DrawPosition(FALSE, NULL);\r
3283     }\r
3284     return TRUE;\r
3285   }\r
3286   return FALSE;\r
3287 }\r
3288 \r
3289 /* Pop up promotion dialog */\r
3290 VOID\r
3291 PromotionPopup(HWND hwnd)\r
3292 {\r
3293   FARPROC lpProc;\r
3294 \r
3295   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
3296   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
3297     hwnd, (DLGPROC)lpProc);\r
3298   FreeProcInstance(lpProc);\r
3299 }\r
3300 \r
3301 /* Toggle ShowThinking */\r
3302 VOID\r
3303 ToggleShowThinking()\r
3304 {\r
3305   ShowThinkingEvent(!appData.showThinking);\r
3306 }\r
3307 \r
3308 VOID\r
3309 LoadGameDialog(HWND hwnd, char* title)\r
3310 {\r
3311   UINT number = 0;\r
3312   FILE *f;\r
3313   char fileTitle[MSG_SIZ];\r
3314   f = OpenFileDialog(hwnd, FALSE, "",\r
3315                      appData.oldSaveStyle ? "gam" : "pgn",\r
3316                      GAME_FILT,\r
3317                      title, &number, fileTitle, NULL);\r
3318   if (f != NULL) {\r
3319     cmailMsgLoaded = FALSE;\r
3320     if (number == 0) {\r
3321       int error = GameListBuild(f);\r
3322       if (error) {\r
3323         DisplayError("Cannot build game list", error);\r
3324       } else if (!ListEmpty(&gameList) &&\r
3325                  ((ListGame *) gameList.tailPred)->number > 1) {\r
3326         GameListPopUp(f, fileTitle);\r
3327         return;\r
3328       }\r
3329       GameListDestroy();\r
3330       number = 1;\r
3331     }\r
3332     LoadGame(f, number, fileTitle, FALSE);\r
3333   }\r
3334 }\r
3335 \r
3336 VOID\r
3337 ChangedConsoleFont()\r
3338 {\r
3339   CHARFORMAT cfmt;\r
3340   CHARRANGE tmpsel, sel;\r
3341   MyFont *f = font[boardSize][CONSOLE_FONT];\r
3342   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3343   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3344   PARAFORMAT paraf;\r
3345 \r
3346   cfmt.cbSize = sizeof(CHARFORMAT);\r
3347   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
3348   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
3349   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
3350    * size.  This was undocumented in the version of MSVC++ that I had\r
3351    * when I wrote the code, but is apparently documented now.\r
3352    */\r
3353   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
3354   cfmt.bCharSet = f->lf.lfCharSet;\r
3355   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
3356   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
3357   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
3358   /* Why are the following seemingly needed too? */\r
3359   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
3360   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
3361   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
3362   tmpsel.cpMin = 0;\r
3363   tmpsel.cpMax = -1; /*999999?*/\r
3364   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
3365   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
3366   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
3367    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
3368    */\r
3369   paraf.cbSize = sizeof(paraf);\r
3370   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
3371   paraf.dxStartIndent = 0;\r
3372   paraf.dxOffset = WRAP_INDENT;\r
3373   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
3374   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
3375 }\r
3376 \r
3377 /*---------------------------------------------------------------------------*\\r
3378  *\r
3379  * Window Proc for main window\r
3380  *\r
3381 \*---------------------------------------------------------------------------*/\r
3382 \r
3383 /* Process messages for main window, etc. */\r
3384 LRESULT CALLBACK\r
3385 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3386 {\r
3387   FARPROC lpProc;\r
3388   int wmId, wmEvent;\r
3389   char *defName;\r
3390   FILE *f;\r
3391   UINT number;\r
3392   char fileTitle[MSG_SIZ];\r
3393 \r
3394   switch (message) {\r
3395 \r
3396   case WM_PAINT: /* message: repaint portion of window */\r
3397     PaintProc(hwnd);\r
3398     break;\r
3399 \r
3400   case WM_ERASEBKGND:\r
3401     if (IsIconic(hwnd)) {\r
3402       /* Cheat; change the message */\r
3403       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
3404     } else {\r
3405       return (DefWindowProc(hwnd, message, wParam, lParam));\r
3406     }\r
3407     break;\r
3408 \r
3409   case WM_LBUTTONDOWN:\r
3410   case WM_MBUTTONDOWN:\r
3411   case WM_RBUTTONDOWN:\r
3412   case WM_LBUTTONUP:\r
3413   case WM_MBUTTONUP:\r
3414   case WM_RBUTTONUP:\r
3415   case WM_MOUSEMOVE:\r
3416     MouseEvent(hwnd, message, wParam, lParam);\r
3417     break;\r
3418 \r
3419   case WM_CHAR:\r
3420     \r
3421     if (appData.icsActive) {\r
3422       if (wParam == '\t') {\r
3423         if (GetKeyState(VK_SHIFT) < 0) {\r
3424           /* shifted */\r
3425           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3426           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3427           SetFocus(h);\r
3428         } else {\r
3429           /* unshifted */\r
3430           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3431           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3432           SetFocus(h);\r
3433         }\r
3434       } else {\r
3435         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3436         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3437         SetFocus(h);\r
3438         SendMessage(h, message, wParam, lParam);\r
3439       }\r
3440     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
3441       PopUpMoveDialog((char)wParam);\r
3442     }\r
3443     break;\r
3444 \r
3445   case WM_PALETTECHANGED:\r
3446     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
3447       int nnew;\r
3448       HDC hdc = GetDC(hwndMain);\r
3449       SelectPalette(hdc, hPal, TRUE);\r
3450       nnew = RealizePalette(hdc);\r
3451       if (nnew > 0) {\r
3452         paletteChanged = TRUE;\r
3453 #if 0\r
3454         UpdateColors(hdc);\r
3455 #else\r
3456         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
3457 #endif\r
3458       }\r
3459       ReleaseDC(hwnd, hdc);\r
3460     }\r
3461     break;\r
3462 \r
3463   case WM_QUERYNEWPALETTE:\r
3464     if (!appData.monoMode /*&& paletteChanged*/) {\r
3465       int nnew;\r
3466       HDC hdc = GetDC(hwndMain);\r
3467       paletteChanged = FALSE;\r
3468       SelectPalette(hdc, hPal, FALSE);\r
3469       nnew = RealizePalette(hdc);\r
3470       if (nnew > 0) {\r
3471         InvalidateRect(hwnd, &boardRect, FALSE);\r
3472       }\r
3473       ReleaseDC(hwnd, hdc);\r
3474       return TRUE;\r
3475     }\r
3476     return FALSE;\r
3477 \r
3478   case WM_COMMAND: /* message: command from application menu */\r
3479     wmId    = LOWORD(wParam);\r
3480     wmEvent = HIWORD(wParam);\r
3481 \r
3482     switch (wmId) {\r
3483     case IDM_NewGame:\r
3484       ResetGameEvent();\r
3485       AnalysisPopDown();\r
3486       break;\r
3487 \r
3488     case IDM_LoadGame:\r
3489       LoadGameDialog(hwnd, "Load Game from File");\r
3490       break;\r
3491 \r
3492     case IDM_LoadNextGame:\r
3493       ReloadGame(1);\r
3494       break;\r
3495 \r
3496     case IDM_LoadPrevGame:\r
3497       ReloadGame(-1);\r
3498       break;\r
3499 \r
3500     case IDM_ReloadGame:\r
3501       ReloadGame(0);\r
3502       break;\r
3503 \r
3504     case IDM_LoadPosition:\r
3505       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
3506         Reset(FALSE, TRUE);\r
3507       }\r
3508       number = 1;\r
3509       f = OpenFileDialog(hwnd, FALSE, "",\r
3510                          appData.oldSaveStyle ? "pos" : "fen",\r
3511                          POSITION_FILT,\r
3512                          "Load Position from File", &number, fileTitle, NULL);\r
3513       if (f != NULL) {\r
3514         LoadPosition(f, number, fileTitle);\r
3515       }\r
3516       break;\r
3517 \r
3518     case IDM_LoadNextPosition:\r
3519       ReloadPosition(1);\r
3520       break;\r
3521 \r
3522     case IDM_LoadPrevPosition:\r
3523       ReloadPosition(-1);\r
3524       break;\r
3525 \r
3526     case IDM_ReloadPosition:\r
3527       ReloadPosition(0);\r
3528       break;\r
3529 \r
3530     case IDM_SaveGame:\r
3531       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
3532       f = OpenFileDialog(hwnd, TRUE, defName,\r
3533                          appData.oldSaveStyle ? "gam" : "pgn",\r
3534                          GAME_FILT,\r
3535                          "Save Game to File", NULL, fileTitle, NULL);\r
3536       if (f != NULL) {\r
3537         SaveGame(f, 0, "");\r
3538       }\r
3539       break;\r
3540 \r
3541     case IDM_SavePosition:\r
3542       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
3543       f = OpenFileDialog(hwnd, TRUE, defName,\r
3544                          appData.oldSaveStyle ? "pos" : "fen",\r
3545                          POSITION_FILT,\r
3546                          "Save Position to File", NULL, fileTitle, NULL);\r
3547       if (f != NULL) {\r
3548         SavePosition(f, 0, "");\r
3549       }\r
3550       break;\r
3551 \r
3552     case IDM_CopyGame:\r
3553       CopyGameToClipboard();\r
3554       break;\r
3555 \r
3556     case IDM_PasteGame:\r
3557       PasteGameFromClipboard();\r
3558       break;\r
3559 \r
3560     case IDM_CopyPosition:\r
3561       CopyFENToClipboard();\r
3562       break;\r
3563 \r
3564     case IDM_PastePosition:\r
3565       PasteFENFromClipboard();\r
3566       break;\r
3567 \r
3568     case IDM_MailMove:\r
3569       MailMoveEvent();\r
3570       break;\r
3571 \r
3572     case IDM_ReloadCMailMsg:\r
3573       Reset(TRUE, TRUE);\r
3574       ReloadCmailMsgEvent(FALSE);\r
3575       break;\r
3576 \r
3577     case IDM_Minimize:\r
3578       ShowWindow(hwnd, SW_MINIMIZE);\r
3579       break;\r
3580 \r
3581     case IDM_Exit:\r
3582       ExitEvent(0);\r
3583       break;\r
3584 \r
3585     case IDM_MachineWhite:\r
3586       MachineWhiteEvent();\r
3587       /*\r
3588        * refresh the tags dialog only if it's visible\r
3589        */\r
3590       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
3591           char *tags;\r
3592           tags = PGNTags(&gameInfo);\r
3593           TagsPopUp(tags, CmailMsg());\r
3594           free(tags);\r
3595       }\r
3596       break;\r
3597 \r
3598     case IDM_MachineBlack:\r
3599       MachineBlackEvent();\r
3600       /*\r
3601        * refresh the tags dialog only if it's visible\r
3602        */\r
3603       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
3604           char *tags;\r
3605           tags = PGNTags(&gameInfo);\r
3606           TagsPopUp(tags, CmailMsg());\r
3607           free(tags);\r
3608       }\r
3609       break;\r
3610 \r
3611     case IDM_TwoMachines:\r
3612       TwoMachinesEvent();\r
3613       /*\r
3614        * refresh the tags dialog only if it's visible\r
3615        */\r
3616       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
3617           char *tags;\r
3618           tags = PGNTags(&gameInfo);\r
3619           TagsPopUp(tags, CmailMsg());\r
3620           free(tags);\r
3621       }\r
3622       break;\r
3623 \r
3624     case IDM_AnalysisMode:\r
3625       if (!first.analysisSupport) {\r
3626         char buf[MSG_SIZ];\r
3627         sprintf(buf, "%s does not support analysis", first.tidy);\r
3628         DisplayError(buf, 0);\r
3629       } else {\r
3630         if (!appData.showThinking) ToggleShowThinking();\r
3631         AnalyzeModeEvent();\r
3632       }\r
3633       break;\r
3634 \r
3635     case IDM_AnalyzeFile:\r
3636       if (!first.analysisSupport) {\r
3637         char buf[MSG_SIZ];\r
3638         sprintf(buf, "%s does not support analysis", first.tidy);\r
3639         DisplayError(buf, 0);\r
3640       } else {\r
3641         if (!appData.showThinking) ToggleShowThinking();\r
3642         AnalyzeFileEvent();\r
3643         LoadGameDialog(hwnd, "Analyze Game from File");\r
3644         AnalysisPeriodicEvent(1);\r
3645       }\r
3646       break;\r
3647 \r
3648     case IDM_IcsClient:\r
3649       IcsClientEvent();\r
3650       break;\r
3651 \r
3652     case IDM_EditGame:\r
3653       EditGameEvent();\r
3654       break;\r
3655 \r
3656     case IDM_EditPosition:\r
3657       EditPositionEvent();\r
3658       break;\r
3659 \r
3660     case IDM_Training:\r
3661       TrainingEvent();\r
3662       break;\r
3663 \r
3664     case IDM_ShowGameList:\r
3665       ShowGameListProc();\r
3666       break;\r
3667 \r
3668     case IDM_EditTags:\r
3669       EditTagsProc();\r
3670       break;\r
3671 \r
3672     case IDM_EditComment:\r
3673       if (commentDialogUp && editComment) {\r
3674         CommentPopDown();\r
3675       } else {\r
3676         EditCommentEvent();\r
3677       }\r
3678       break;\r
3679 \r
3680     case IDM_Pause:\r
3681       PauseEvent();\r
3682       break;\r
3683 \r
3684     case IDM_Accept:\r
3685       AcceptEvent();\r
3686       break;\r
3687 \r
3688     case IDM_Decline:\r
3689       DeclineEvent();\r
3690       break;\r
3691 \r
3692     case IDM_Rematch:\r
3693       RematchEvent();\r
3694       break;\r
3695 \r
3696     case IDM_CallFlag:\r
3697       CallFlagEvent();\r
3698       break;\r
3699 \r
3700     case IDM_Draw:\r
3701       DrawEvent();\r
3702       break;\r
3703 \r
3704     case IDM_Adjourn:\r
3705       AdjournEvent();\r
3706       break;\r
3707 \r
3708     case IDM_Abort:\r
3709       AbortEvent();\r
3710       break;\r
3711 \r
3712     case IDM_Resign:\r
3713       ResignEvent();\r
3714       break;\r
3715 \r
3716     case IDM_StopObserving:\r
3717       StopObservingEvent();\r
3718       break;\r
3719 \r
3720     case IDM_StopExamining:\r
3721       StopExaminingEvent();\r
3722       break;\r
3723 \r
3724     case IDM_TypeInMove:\r
3725       PopUpMoveDialog('\000');\r
3726       break;\r
3727 \r
3728     case IDM_Backward:\r
3729       BackwardEvent();\r
3730       SetFocus(hwndMain);\r
3731       break;\r
3732 \r
3733     case IDM_Forward:\r
3734       ForwardEvent();\r
3735       SetFocus(hwndMain);\r
3736       break;\r
3737 \r
3738     case IDM_ToStart:\r
3739       ToStartEvent();\r
3740       SetFocus(hwndMain);\r
3741       break;\r
3742 \r
3743     case IDM_ToEnd:\r
3744       ToEndEvent();\r
3745       SetFocus(hwndMain);\r
3746       break;\r
3747 \r
3748     case IDM_Revert:\r
3749       RevertEvent();\r
3750       break;\r
3751 \r
3752     case IDM_TruncateGame:\r
3753       TruncateGameEvent();\r
3754       break;\r
3755 \r
3756     case IDM_MoveNow:\r
3757       MoveNowEvent();\r
3758       break;\r
3759 \r
3760     case IDM_RetractMove:\r
3761       RetractMoveEvent();\r
3762       break;\r
3763 \r
3764     case IDM_FlipView:\r
3765       flipView = !flipView;\r
3766       DrawPosition(FALSE, NULL);\r
3767       break;\r
3768 \r
3769     case IDM_GeneralOptions:\r
3770       GeneralOptionsPopup(hwnd);\r
3771