68dab6659b58015563697b5e9b6177d5c94051fb
[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 #include <winsock.h>\r
57 \r
58 #include <stdio.h>\r
59 #include <stdlib.h>\r
60 #include <malloc.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 #include <mmsystem.h>\r
68 \r
69 #if __GNUC__\r
70 #include <errno.h>\r
71 #include <string.h>\r
72 #endif\r
73 \r
74 #include "common.h"\r
75 #include "winboard.h"\r
76 #include "frontend.h"\r
77 #include "backend.h"\r
78 #include "moves.h"\r
79 #include "wclipbrd.h"\r
80 #include "wgamelist.h"\r
81 #include "wedittags.h"\r
82 #include "woptions.h"\r
83 #include "wsockerr.h"\r
84 #include "defaults.h"\r
85 \r
86 typedef struct {\r
87   ChessSquare piece;  \r
88   POINT pos;      /* window coordinates of current pos */\r
89   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
90   POINT from;     /* board coordinates of the piece's orig pos */\r
91   POINT to;       /* board coordinates of the piece's new pos */\r
92 } AnimInfo;\r
93 \r
94 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
95 \r
96 typedef struct {\r
97   POINT start;    /* window coordinates of start pos */\r
98   POINT pos;      /* window coordinates of current pos */\r
99   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
100   POINT from;     /* board coordinates of the piece's orig pos */\r
101 } DragInfo;\r
102 \r
103 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
104 \r
105 typedef struct {\r
106   POINT sq[2];    /* board coordinates of from, to squares */\r
107 } HighlightInfo;\r
108 \r
109 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
110 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
111 \r
112 /* Window class names */\r
113 char szAppName[] = "WinBoard";\r
114 char szConsoleName[] = "WBConsole";\r
115 \r
116 /* Title bar text */\r
117 char szTitle[] = "WinBoard";\r
118 char szConsoleTitle[] = "ICS Interaction";\r
119 \r
120 char *programName;\r
121 char *settingsFileName;\r
122 BOOLEAN saveSettingsOnExit;\r
123 char installDir[MSG_SIZ];\r
124 \r
125 BoardSize boardSize;\r
126 BOOLEAN chessProgram;\r
127 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
128 static int squareSize, lineGap;\r
129 static int winWidth, winHeight;\r
130 static RECT messageRect, whiteRect, blackRect;\r
131 static char messageText[MESSAGE_TEXT_MAX];\r
132 static int clockTimerEvent = 0;\r
133 static int loadGameTimerEvent = 0;\r
134 static int analysisTimerEvent = 0;\r
135 static DelayedEventCallback delayedTimerCallback;\r
136 static int delayedTimerEvent = 0;\r
137 static int buttonCount = 2;\r
138 char *icsTextMenuString;\r
139 char *icsNames;\r
140 char *firstChessProgramNames;\r
141 char *secondChessProgramNames;\r
142 \r
143 #define ARG_MAX 20000\r
144 \r
145 #define PALETTESIZE 256\r
146 \r
147 HINSTANCE hInst;          /* current instance */\r
148 HWND hwndMain = NULL;        /* root window*/\r
149 HWND hwndConsole = NULL;\r
150 BOOLEAN alwaysOnTop = FALSE;\r
151 RECT boardRect;\r
152 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
153   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
154 HPALETTE hPal;\r
155 ColorClass currentColorClass;\r
156 \r
157 HWND hCommPort = NULL;    /* currently open comm port */\r
158 static HWND hwndPause;    /* pause button */\r
159 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];\r
160 static HBRUSH lightSquareBrush, darkSquareBrush,\r
161   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
162 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
163 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
164 static HPEN gridPen = NULL;\r
165 static HPEN highlightPen = NULL;\r
166 static HPEN premovePen = NULL;\r
167 static NPLOGPALETTE pLogPal;\r
168 static BOOL paletteChanged = FALSE;\r
169 static HICON iconWhite, iconBlack, iconCurrent;\r
170 static int doingSizing = FALSE;\r
171 static int lastSizing = 0;\r
172 static int prevStderrPort;\r
173 \r
174 #if __GNUC__ && !defined(_winmajor)\r
175 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
176 #else\r
177 #define oldDialog (_winmajor < 4)\r
178 #endif\r
179 \r
180 char *defaultTextAttribs[] = \r
181 {\r
182   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
183   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
184   COLOR_NONE\r
185 };\r
186 \r
187 typedef struct {\r
188   char *name;\r
189   int squareSize;\r
190   int lineGap;\r
191   int smallLayout;\r
192   int tinyLayout;\r
193   int cliWidth, cliHeight;\r
194 } SizeInfo;\r
195 \r
196 SizeInfo sizeInfo[] = \r
197 {\r
198   { "tiny",     21, 0, 1, 1, 0, 0 },\r
199   { "teeny",    25, 1, 1, 1, 0, 0 },\r
200   { "dinky",    29, 1, 1, 1, 0, 0 },\r
201   { "petite",   33, 1, 1, 1, 0, 0 },\r
202   { "slim",     37, 2, 1, 0, 0, 0 },\r
203   { "small",    40, 2, 1, 0, 0, 0 },\r
204   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
205   { "middling", 49, 2, 0, 0, 0, 0 },\r
206   { "average",  54, 2, 0, 0, 0, 0 },\r
207   { "moderate", 58, 3, 0, 0, 0, 0 },\r
208   { "medium",   64, 3, 0, 0, 0, 0 },\r
209   { "bulky",    72, 3, 0, 0, 0, 0 },\r
210   { "large",    80, 3, 0, 0, 0, 0 },\r
211   { "big",      87, 3, 0, 0, 0, 0 },\r
212   { "huge",     95, 3, 0, 0, 0, 0 },\r
213   { "giant",    108, 3, 0, 0, 0, 0 },\r
214   { "colossal", 116, 4, 0, 0, 0, 0 },\r
215   { "titanic",  129, 4, 0, 0, 0, 0 },\r
216   { NULL, 0, 0, 0, 0, 0, 0 }\r
217 };\r
218 \r
219 #define MF(x) {x, {0, }, {0, }, 0}\r
220 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
221 {\r
222   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), \r
223     MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),\r
224     MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },\r
225   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), \r
226     MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),\r
227     MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },\r
228   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),\r
229     MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),\r
230     MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },\r
231   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),\r
232     MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),\r
233     MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },\r
234   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),\r
235     MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),\r
236     MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },\r
237   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),\r
238     MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),\r
239     MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },\r
240   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),\r
241     MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),\r
242     MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },\r
243   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),\r
244     MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),\r
245     MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },\r
246   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),\r
247     MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),\r
248     MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },\r
249   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),\r
250     MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),\r
251     MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },\r
252   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),\r
253     MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),\r
254     MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },\r
255   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),\r
256     MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),\r
257     MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },\r
258   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),\r
259     MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),\r
260     MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },\r
261   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),\r
262     MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),\r
263     MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },\r
264   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),\r
265     MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),\r
266     MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },\r
267   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),\r
268     MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),\r
269     MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },\r
270   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),\r
271     MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),\r
272     MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },\r
273   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),\r
274     MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),\r
275     MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },\r
276 };\r
277 \r
278 MyFont *font[NUM_SIZES][NUM_FONTS];\r
279 \r
280 typedef struct {\r
281   char *label;\r
282   int id;\r
283   HWND hwnd;\r
284   WNDPROC wndproc;\r
285 } MyButtonDesc;\r
286 \r
287 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
288 #define N_BUTTONS 5\r
289 \r
290 MyButtonDesc buttonDesc[N_BUTTONS] =\r
291 {\r
292   {"<<", IDM_ToStart, NULL, NULL},\r
293   {"<", IDM_Backward, NULL, NULL},\r
294   {"P", IDM_Pause, NULL, NULL},\r
295   {">", IDM_Forward, NULL, NULL},\r
296   {">>", IDM_ToEnd, NULL, NULL},\r
297 };\r
298 \r
299 int tinyLayout = 0, smallLayout = 0;\r
300 #define MENU_BAR_ITEMS 6\r
301 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
302   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
303   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
304 };\r
305 \r
306 \r
307 MySound sounds[(int)NSoundClasses];\r
308 MyTextAttribs textAttribs[(int)NColorClasses];\r
309 \r
310 MyColorizeAttribs colorizeAttribs[] = {\r
311   { (COLORREF)0, 0, "Shout Text" },\r
312   { (COLORREF)0, 0, "SShout/CShout" },\r
313   { (COLORREF)0, 0, "Channel 1 Text" },\r
314   { (COLORREF)0, 0, "Channel Text" },\r
315   { (COLORREF)0, 0, "Kibitz Text" },\r
316   { (COLORREF)0, 0, "Tell Text" },\r
317   { (COLORREF)0, 0, "Challenge Text" },\r
318   { (COLORREF)0, 0, "Request Text" },\r
319   { (COLORREF)0, 0, "Seek Text" },\r
320   { (COLORREF)0, 0, "Normal Text" },\r
321   { (COLORREF)0, 0, "None" }\r
322 };\r
323 \r
324 \r
325 \r
326 static char *commentTitle;\r
327 static char *commentText;\r
328 static int commentIndex;\r
329 static Boolean editComment = FALSE;\r
330 HWND commentDialog = NULL;\r
331 BOOLEAN commentDialogUp = FALSE;\r
332 static int commentX, commentY, commentH, commentW;\r
333 \r
334 static char *analysisTitle;\r
335 static char *analysisText;\r
336 HWND analysisDialog = NULL;\r
337 BOOLEAN analysisDialogUp = FALSE;\r
338 static int analysisX, analysisY, analysisH, analysisW;\r
339 \r
340 char errorMessage[2*MSG_SIZ];\r
341 HWND errorDialog = NULL;\r
342 BOOLEAN moveErrorMessageUp = FALSE;\r
343 BOOLEAN consoleEcho = TRUE;\r
344 CHARFORMAT consoleCF;\r
345 COLORREF consoleBackgroundColor;\r
346 \r
347 char *programVersion;\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        gameInfo.variant == VariantGiveaway) ?\r
3251                SW_SHOW : SW_HIDE);\r
3252     return TRUE;\r
3253 \r
3254   case WM_COMMAND: /* message: received a command */\r
3255     switch (LOWORD(wParam)) {\r
3256     case IDCANCEL:\r
3257       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3258       ClearHighlights();\r
3259       DrawPosition(FALSE, NULL);\r
3260       return TRUE;\r
3261     case PB_King:\r
3262       promoChar = 'k';\r
3263       break;\r
3264     case PB_Queen:\r
3265       promoChar = 'q';\r
3266       break;\r
3267     case PB_Rook:\r
3268       promoChar = 'r';\r
3269       break;\r
3270     case PB_Bishop:\r
3271       promoChar = 'b';\r
3272       break;\r
3273     case PB_Knight:\r
3274       promoChar = 'n';\r
3275       break;\r
3276     default:\r
3277       return FALSE;\r
3278     }\r
3279     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3280     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
3281     if (!appData.highlightLastMove) {\r
3282       ClearHighlights();\r
3283       DrawPosition(FALSE, NULL);\r
3284     }\r
3285     return TRUE;\r
3286   }\r
3287   return FALSE;\r
3288 }\r
3289 \r
3290 /* Pop up promotion dialog */\r
3291 VOID\r
3292 PromotionPopup(HWND hwnd)\r
3293 {\r
3294   FARPROC lpProc;\r
3295 \r
3296   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
3297   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
3298     hwnd, (DLGPROC)lpProc);\r
3299   FreeProcInstance(lpProc);\r
3300 }\r
3301 \r
3302 /* Toggle ShowThinking */\r
3303 VOID\r
3304 ToggleShowThinking()\r
3305 {\r
3306   ShowThinkingEvent(!appData.showThinking);\r
3307 }\r
3308 \r
3309 VOID\r
3310 LoadGameDialog(HWND hwnd, char* title)\r
3311 {\r
3312   UINT number = 0;\r
3313   FILE *f;\r
3314   char fileTitle[MSG_SIZ];\r
3315   f = OpenFileDialog(hwnd, FALSE, "",\r
3316                      appData.oldSaveStyle ? "gam" : "pgn",\r
3317                      GAME_FILT,\r
3318                      title, &number, fileTitle, NULL);\r
3319   if (f != NULL) {\r
3320     cmailMsgLoaded = FALSE;\r
3321     if (number == 0) {\r
3322       int error = GameListBuild(f);\r
3323       if (error) {\r
3324         DisplayError("Cannot build game list", error);\r
3325       } else if (!ListEmpty(&gameList) &&\r
3326                  ((ListGame *) gameList.tailPred)->number > 1) {\r
3327         GameListPopUp(f, fileTitle);\r
3328         return;\r
3329       }\r
3330       GameListDestroy();\r
3331       number = 1;\r
3332     }\r
3333     LoadGame(f, number, fileTitle, FALSE);\r
3334   }\r
3335 }\r
3336 \r
3337 VOID\r
3338 ChangedConsoleFont()\r
3339 {\r
3340   CHARFORMAT cfmt;\r
3341   CHARRANGE tmpsel, sel;\r
3342   MyFont *f = font[boardSize][CONSOLE_FONT];\r
3343   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3344   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3345   PARAFORMAT paraf;\r
3346 \r
3347   cfmt.cbSize = sizeof(CHARFORMAT);\r
3348   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
3349   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
3350   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
3351    * size.  This was undocumented in the version of MSVC++ that I had\r
3352    * when I wrote the code, but is apparently documented now.\r
3353    */\r
3354   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
3355   cfmt.bCharSet = f->lf.lfCharSet;\r
3356   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
3357   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
3358   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
3359   /* Why are the following seemingly needed too? */\r
3360   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
3361   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
3362   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
3363   tmpsel.cpMin = 0;\r
3364   tmpsel.cpMax = -1; /*999999?*/\r
3365   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
3366   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
3367   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
3368    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
3369    */\r
3370   paraf.cbSize = sizeof(paraf);\r
3371   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
3372   paraf.dxStartIndent = 0;\r
3373   paraf.dxOffset = WRAP_INDENT;\r
3374   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
3375   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
3376 }\r
3377 \r
3378 /*---------------------------------------------------------------------------*\\r
3379  *\r
3380  * Window Proc for main window\r
3381  *\r
3382 \*---------------------------------------------------------------------------*/\r
3383 \r
3384 /* Process messages for main window, etc. */\r
3385 LRESULT CALLBACK\r
3386 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3387 {\r
3388   FARPROC lpProc;\r
3389   int wmId, wmEvent;\r
3390   char *defName;\r
3391   FILE *f;\r
3392   UINT number;\r
3393   char fileTitle[MSG_SIZ];\r
3394 \r
3395   switch (message) {\r
3396 \r
3397   case WM_PAINT: /* message: repaint portion of window */\r
3398     PaintProc(hwnd);\r
3399     break;\r
3400 \r
3401   case WM_ERASEBKGND:\r
3402     if (IsIconic(hwnd)) {\r
3403       /* Cheat; change the message */\r
3404       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
3405     } else {\r
3406       return (DefWindowProc(hwnd, message, wParam, lParam));\r
3407     }\r
3408     break;\r
3409 \r
3410   case WM_LBUTTONDOWN:\r
3411   case WM_MBUTTONDOWN:\r
3412   case WM_RBUTTONDOWN:\r
3413   case WM_LBUTTONUP:\r
3414   case WM_MBUTTONUP:\r
3415   case WM_RBUTTONUP:\r
3416   case WM_MOUSEMOVE:\r
3417     MouseEvent(hwnd, message, wParam, lParam);\r
3418     break;\r
3419 \r
3420   case WM_CHAR:\r
3421     \r
3422     if (appData.icsActive) {\r
3423       if (wParam == '\t') {\r
3424         if (GetKeyState(VK_SHIFT) < 0) {\r
3425           /* shifted */\r
3426           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3427           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3428           SetFocus(h);\r
3429         } else {\r
3430           /* unshifted */\r
3431           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3432           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3433           SetFocus(h);\r
3434         }\r
3435       } else {\r
3436         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3437         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3438         SetFocus(h);\r
3439         SendMessage(h, message, wParam, lParam);\r
3440       }\r
3441     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
3442       PopUpMoveDialog((char)wParam);\r
3443     }\r
3444     break;\r
3445 \r
3446   case WM_PALETTECHANGED:\r
3447     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
3448       int nnew;\r
3449       HDC hdc = GetDC(hwndMain);\r
3450       SelectPalette(hdc, hPal, TRUE);\r
3451       nnew = RealizePalette(hdc);\r
3452       if (nnew > 0) {\r
3453         paletteChanged = TRUE;\r
3454 #if 0\r
3455         UpdateColors(hdc);\r
3456 #else\r
3457         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
3458 #endif\r
3459       }\r
3460       ReleaseDC(hwnd, hdc);\r
3461     }\r
3462     break;\r
3463 \r
3464   case WM_QUERYNEWPALETTE:\r
3465     if (!appData.monoMode /*&& paletteChanged*/) {\r
3466       int nnew;\r
3467       HDC hdc = GetDC(hwndMain);\r
3468       paletteChanged = FALSE;\r
3469       SelectPalette(hdc, hPal, FALSE);\r
3470       nnew = RealizePalette(hdc);\r
3471       if (nnew > 0) {\r
3472         InvalidateRect(hwnd, &boardRect, FALSE);\r
3473       }\r
3474       ReleaseDC(hwnd, hdc);\r
3475       return TRUE;\r
3476     }\r
3477     return FALSE;\r
3478 \r
3479   case WM_COMMAND: /* message: command from application menu */\r
3480     wmId    = LOWORD(wParam);\r
3481     wmEvent = HIWORD(wParam);\r
3482 \r
3483     switch (wmId) {\r
3484     case IDM_NewGame:\r
3485       ResetGameEvent();\r
3486       AnalysisPopDown();\r
3487       break;\r
3488 \r
3489     case IDM_LoadGame:\r
3490       LoadGameDialog(hwnd, "Load Game from File");\r
3491       break;\r
3492 \r
3493     case IDM_LoadNextGame:\r
3494       ReloadGame(1);\r
3495       break;\r
3496 \r
3497     case IDM_LoadPrevGame:\r
3498       ReloadGame(-1);\r
3499       break;\r
3500 \r
3501     case IDM_ReloadGame:\r
3502       ReloadGame(0);\r
3503       break;\r
3504 \r
3505     case IDM_LoadPosition:\r
3506       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
3507         Reset(FALSE, TRUE);\r
3508       }\r
3509       number = 1;\r
3510       f = OpenFileDialog(hwnd, FALSE, "",\r
3511                          appData.oldSaveStyle ? "pos" : "fen",\r
3512                          POSITION_FILT,\r
3513                          "Load Position from File", &number, fileTitle, NULL);\r
3514       if (f != NULL) {\r
3515         LoadPosition(f, number, fileTitle);\r
3516       }\r
3517       break;\r
3518 \r
3519     case IDM_LoadNextPosition:\r
3520       ReloadPosition(1);\r
3521       break;\r
3522 \r
3523     case IDM_LoadPrevPosition:\r
3524       ReloadPosition(-1);\r
3525       break;\r
3526 \r
3527     case IDM_ReloadPosition:\r
3528       ReloadPosition(0);\r
3529       break;\r
3530 \r
3531     case IDM_SaveGame:\r
3532       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
3533       f = OpenFileDialog(hwnd, TRUE, defName,\r
3534                          appData.oldSaveStyle ? "gam" : "pgn",\r
3535                          GAME_FILT,\r
3536                          "Save Game to File", NULL, fileTitle, NULL);\r
3537       if (f != NULL) {\r
3538         SaveGame(f, 0, "");\r
3539       }\r
3540       break;\r
3541 \r
3542     case IDM_SavePosition:\r
3543       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
3544       f = OpenFileDialog(hwnd, TRUE, defName,\r
3545                          appData.oldSaveStyle ? "pos" : "fen",\r
3546                          POSITION_FILT,\r
3547                          "Save Position to File", NULL, fileTitle, NULL);\r
3548       if (f != NULL) {\r
3549         SavePosition(f, 0, "");\r
3550       }\r
3551       break;\r
3552 \r
3553     case IDM_CopyGame:\r
3554       CopyGameToClipboard();\r
3555       break;\r
3556 \r
3557     case IDM_PasteGame:\r
3558       PasteGameFromClipboard();\r
3559       break;\r
3560 \r
3561     case IDM_CopyPosition:\r
3562       CopyFENToClipboard();\r
3563       break;\r
3564 \r
3565     case IDM_PastePosition:\r
3566       PasteFENFromClipboard();\r
3567       break;\r
3568 \r
3569     case IDM_MailMove:\r
3570       MailMoveEvent();\r
3571       break;\r
3572 \r
3573     case IDM_ReloadCMailMsg:\r
3574       Reset(TRUE, TRUE);\r
3575       ReloadCmailMsgEvent(FALSE);\r
3576       break;\r
3577 \r
3578     case IDM_Minimize:\r
3579       ShowWindow(hwnd, SW_MINIMIZE);\r
3580       break;\r
3581 \r
3582     case IDM_Exit:\r
3583       ExitEvent(0);\r
3584       break;\r
3585 \r
3586     case IDM_MachineWhite:\r
3587       MachineWhiteEvent();\r
3588       /*\r
3589        * refresh the tags dialog only if it's visible\r
3590        */\r
3591       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
3592           char *tags;\r
3593           tags = PGNTags(&gameInfo);\r
3594           TagsPopUp(tags, CmailMsg());\r
3595           free(tags);\r
3596       }\r
3597       break;\r
3598 \r
3599     case IDM_MachineBlack:\r
3600       MachineBlackEvent();\r
3601       /*\r
3602        * refresh the tags dialog only if it's visible\r
3603        */\r
3604       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
3605           char *tags;\r
3606           tags = PGNTags(&gameInfo);\r
3607           TagsPopUp(tags, CmailMsg());\r
3608           free(tags);\r
3609       }\r
3610       break;\r
3611 \r
3612     case IDM_TwoMachines:\r
3613       TwoMachinesEvent();\r
3614       /*\r
3615        * refresh the tags dialog only if it's visible\r
3616        */\r
3617       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
3618           char *tags;\r
3619           tags = PGNTags(&gameInfo);\r
3620           TagsPopUp(tags, CmailMsg());\r
3621           free(tags);\r
3622       }\r
3623       break;\r
3624 \r
3625     case IDM_AnalysisMode:\r
3626       if (!first.analysisSupport) {\r
3627         char buf[MSG_SIZ];\r
3628         sprintf(buf, "%s does not support analysis", first.tidy);\r
3629         DisplayError(buf, 0);\r
3630       } else {\r
3631         if (!appData.showThinking) ToggleShowThinking();\r
3632         AnalyzeModeEvent();\r
3633       }\r
3634       break;\r
3635 \r
3636     case IDM_AnalyzeFile:\r
3637       if (!first.analysisSupport) {\r
3638         char buf[MSG_SIZ];\r
3639         sprintf(buf, "%s does not support analysis", first.tidy);\r
3640         DisplayError(buf, 0);\r
3641       } else {\r
3642         if (!appData.showThinking) ToggleShowThinking();\r
3643         AnalyzeFileEvent();\r
3644         LoadGameDialog(hwnd, "Analyze Game from File");\r
3645         AnalysisPeriodicEvent(1);\r
3646       }\r
3647       break;\r
3648 \r
3649     case IDM_IcsClient:\r
3650       IcsClientEvent();\r
3651       break;\r
3652 \r
3653     case IDM_EditGame:\r
3654       EditGameEvent();\r
3655       break;\r
3656 \r
3657     case IDM_EditPosition:\r
3658       EditPositionEvent();\r
3659       break;\r
3660 \r
3661     case IDM_Training:\r
3662       TrainingEvent();\r
3663       break;\r
3664 \r
3665     case IDM_ShowGameList:\r
3666       ShowGameListProc();\r
3667       break;\r
3668 \r
3669     case IDM_EditTags:\r
3670       EditTagsProc();\r
3671       break;\r
3672 \r
3673     case IDM_EditComment:\r
3674       if (commentDialogUp && editComment) {\r
3675         CommentPopDown();\r
3676       } else {\r
3677         EditCommentEvent();\r
3678       }\r
3679       break;\r
3680 \r
3681     case IDM_Pause:\r
3682       PauseEvent();\r
3683       break;\r
3684 \r
3685     case IDM_Accept:\r
3686       AcceptEvent();\r
3687       break;\r
3688 \r
3689     case IDM_Decline:\r
3690       DeclineEvent();\r
3691       break;\r
3692 \r
3693     case IDM_Rematch:\r
3694       RematchEvent();\r
3695       break;\r
3696 \r
3697     case IDM_CallFlag:\r
3698       CallFlagEvent();\r
3699       break;\r
3700 \r
3701     case IDM_Draw:\r
3702       DrawEvent();\r
3703       break;\r
3704 \r
3705     case IDM_Adjourn:\r
3706       AdjournEvent();\r
3707       break;\r
3708 \r
3709     case IDM_Abort:\r
3710       AbortEvent();\r
3711       break;\r
3712 \r
3713     case IDM_Resign:\r
3714       ResignEvent();\r
3715       break;\r
3716 \r
3717     case IDM_StopObserving:\r
3718       StopObservingEvent();\r
3719       break;\r
3720 \r
3721     case IDM_StopExamining:\r
3722       StopExaminingEvent();\r
3723       break;\r
3724 \r
3725     case IDM_TypeInMove:\r
3726       PopUpMoveDialog('\000');\r
3727       break;\r
3728 \r
3729     case IDM_Backward:\r
3730       BackwardEvent();\r
3731       SetFocus(hwndMain);\r
3732       break;\r
3733 \r
3734     case IDM_Forward:\r
3735       ForwardEvent();\r
3736       SetFocus(hwndMain);\r
3737       break;\r
3738 \r
3739     case IDM_ToStart:\r
3740       ToStartEvent();\r
3741       SetFocus(hwndMain);\r
3742       break;\r
3743 \r
3744     case IDM_ToEnd:\r
3745       ToEndEvent();\r
3746       SetFocus(hwndMain);\r
3747       break;\r
3748 \r
3749     case IDM_Revert:\r
3750       RevertEvent();\r
3751       break;\r
3752 \r
3753     case IDM_TruncateGame:\r
3754       TruncateGameEvent();\r
3755       break;\r
3756 \r
3757     case IDM_MoveNow:\r
3758       MoveNowEvent();\r
3759       break;\r
3760 \r
3761     case IDM_RetractMove:\r
3762       RetractMoveEvent();\r
3763       break;\r
3764 \r
3765     case IDM_FlipView:\r
3766       flipView = !flipView;\r
3767       DrawPosition(FALSE, NULL);\r
3768       break;\r
3769 \r
3770     case IDM_GeneralOptions:\r
3771       GeneralOptionsPopup(hwnd);\r
3772       break;\r
3773 \r
3774     case IDM_BoardOptions:\r
3775       BoardOptionsPopup(hwnd);\r
3776       break;\r
3777 \r
3778     case IDM_IcsOptions:\r
3779       IcsOptionsPopup(hwnd);\r
3780       break;\r
3781 \r
3782     case IDM_Fonts:\r
3783       FontsOptionsPopup(hwnd);\r
3784       break;\r
3785 \r
3786     case IDM_Sounds:\r
3787       SoundOptionsPopup(hwnd);\r
3788       break;\r
3789 \r
3790     case IDM_CommPort:\r
3791