Bugfix draw offer to engine if color zippy enable.
[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 errorTitle[MSG_SIZ];\r
341 char errorMessage[2*MSG_SIZ];\r
342 HWND errorDialog = NULL;\r
343 BOOLEAN moveErrorMessageUp = FALSE;\r
344 BOOLEAN consoleEcho = TRUE;\r
345 CHARFORMAT consoleCF;\r
346 COLORREF consoleBackgroundColor;\r
347 \r
348 char *programVersion;\r
349 \r
350 #define CPReal 1\r
351 #define CPComm 2\r
352 #define CPSock 3\r
353 #define CPRcmd 4\r
354 typedef int CPKind;\r
355 \r
356 typedef struct {\r
357   CPKind kind;\r
358   HANDLE hProcess;\r
359   DWORD pid;\r
360   HANDLE hTo;\r
361   HANDLE hFrom;\r
362   SOCKET sock;\r
363   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
364 } ChildProc;\r
365 \r
366 #define INPUT_SOURCE_BUF_SIZE 4096\r
367 \r
368 typedef struct _InputSource {\r
369   CPKind kind;\r
370   HANDLE hFile;\r
371   SOCKET sock;\r
372   int lineByLine;\r
373   HANDLE hThread;\r
374   DWORD id;\r
375   char buf[INPUT_SOURCE_BUF_SIZE];\r
376   char *next;\r
377   DWORD count;\r
378   int error;\r
379   InputCallback func;\r
380   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
381   VOIDSTAR closure;\r
382 } InputSource;\r
383 \r
384 InputSource *consoleInputSource;\r
385 \r
386 DCB dcb;\r
387 \r
388 /* forward */\r
389 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
390 VOID ConsoleCreate();\r
391 LRESULT CALLBACK\r
392   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
393 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
394 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
395 VOID ParseCommSettings(char *arg, DCB *dcb);\r
396 LRESULT CALLBACK\r
397   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
398 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
399 void ParseIcsTextMenu(char *icsTextMenuString);\r
400 VOID PopUpMoveDialog(char firstchar);\r
401 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
402 \r
403 /*\r
404  * Setting "frozen" should disable all user input other than deleting\r
405  * the window.  We do this while engines are initializing themselves.\r
406  */\r
407 static int frozen = 0;\r
408 static int oldMenuItemState[MENU_BAR_ITEMS];\r
409 void FreezeUI()\r
410 {\r
411   HMENU hmenu;\r
412   int i;\r
413 \r
414   if (frozen) return;\r
415   frozen = 1;\r
416   hmenu = GetMenu(hwndMain);\r
417   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
418     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
419   }\r
420   DrawMenuBar(hwndMain);\r
421 }\r
422 \r
423 /* Undo a FreezeUI */\r
424 void ThawUI()\r
425 {\r
426   HMENU hmenu;\r
427   int i;\r
428 \r
429   if (!frozen) return;\r
430   frozen = 0;\r
431   hmenu = GetMenu(hwndMain);\r
432   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
433     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
434   }\r
435   DrawMenuBar(hwndMain);\r
436 }\r
437 \r
438 /*---------------------------------------------------------------------------*\\r
439  *\r
440  * WinMain\r
441  *\r
442 \*---------------------------------------------------------------------------*/\r
443 \r
444 int APIENTRY\r
445 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
446         LPSTR lpCmdLine, int nCmdShow)\r
447 {\r
448   MSG msg;\r
449   HANDLE hAccelMain, hAccelNoAlt;\r
450 \r
451   debugFP = stderr;\r
452 \r
453   LoadLibrary("RICHED32.DLL");\r
454   consoleCF.cbSize = sizeof(CHARFORMAT);\r
455 \r
456   if (!InitApplication(hInstance)) {\r
457     return (FALSE);\r
458   }\r
459   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
460     return (FALSE);\r
461   }\r
462 \r
463   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
464   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
465 \r
466   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
467 \r
468   while (GetMessage(&msg, /* message structure */\r
469                     NULL, /* handle of window receiving the message */\r
470                     0,    /* lowest message to examine */\r
471                     0))   /* highest message to examine */\r
472     {\r
473       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
474           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
475           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
476           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
477           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
478           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
479         TranslateMessage(&msg); /* Translates virtual key codes */\r
480         DispatchMessage(&msg);  /* Dispatches message to window */\r
481       }\r
482     }\r
483 \r
484 \r
485   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
486 }\r
487 \r
488 /*---------------------------------------------------------------------------*\\r
489  *\r
490  * Initialization functions\r
491  *\r
492 \*---------------------------------------------------------------------------*/\r
493 \r
494 BOOL\r
495 InitApplication(HINSTANCE hInstance)\r
496 {\r
497   WNDCLASS wc;\r
498 \r
499   /* Fill in window class structure with parameters that describe the */\r
500   /* main window. */\r
501 \r
502   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
503   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
504   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
505   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
506   wc.hInstance     = hInstance;         /* Owner of this class */\r
507   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
508   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
509   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
510   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
511   wc.lpszClassName = szAppName;                 /* Name to register as */\r
512 \r
513   /* Register the window class and return success/failure code. */\r
514   if (!RegisterClass(&wc)) return FALSE;\r
515 \r
516   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
517   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
518   wc.cbClsExtra    = 0;\r
519   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
520   wc.hInstance     = hInstance;\r
521   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
522   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
523   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
524   wc.lpszMenuName  = NULL;\r
525   wc.lpszClassName = szConsoleName;\r
526 \r
527   if (!RegisterClass(&wc)) return FALSE;\r
528   return TRUE;\r
529 }\r
530 \r
531 \r
532 /* Set by InitInstance, used by EnsureOnScreen */\r
533 int screenHeight, screenWidth;\r
534 \r
535 void\r
536 EnsureOnScreen(int *x, int *y)\r
537 {\r
538   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
539   if (*x > screenWidth - 32) *x = 0;\r
540   if (*y > screenHeight - 32) *y = 0;\r
541 }\r
542 \r
543 BOOL\r
544 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
545 {\r
546   HWND hwnd; /* Main window handle. */\r
547   int ibs;\r
548   WINDOWPLACEMENT wp;\r
549   char *filepart;\r
550 \r
551   hInst = hInstance;    /* Store instance handle in our global variable */\r
552 \r
553   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
554     *filepart = NULLCHAR;\r
555   } else {\r
556     GetCurrentDirectory(MSG_SIZ, installDir);\r
557   }\r
558   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
559   if (appData.debugMode) {\r
560     debugFP = fopen("winboard.debug", "w");\r
561     setbuf(debugFP, NULL);\r
562   }\r
563 \r
564   InitBackEnd1();\r
565 \r
566   /* Create a main window for this application instance. */\r
567   hwnd = CreateWindow(szAppName, szTitle,\r
568                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
569                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
570                       NULL, NULL, hInstance, NULL);\r
571   hwndMain = hwnd;\r
572 \r
573   /* If window could not be created, return "failure" */\r
574   if (!hwnd) {\r
575     return (FALSE);\r
576   }\r
577 \r
578   iconWhite = LoadIcon(hInstance, "icon_white");\r
579   iconBlack = LoadIcon(hInstance, "icon_black");\r
580   iconCurrent = iconWhite;\r
581   InitDrawingColors();\r
582   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
583   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
584   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
585     /* Compute window size for each board size, and use the largest\r
586        size that fits on this screen as the default. */\r
587     InitDrawingSizes((BoardSize)ibs, 0);\r
588     if (boardSize == (BoardSize)-1 &&\r
589         winHeight <= screenHeight && winWidth <= screenWidth) {\r
590       boardSize = (BoardSize)ibs;\r
591     }\r
592   }\r
593   InitDrawingSizes(boardSize, 0);\r
594   InitMenuChecks();\r
595   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
596 \r
597   InitBackEnd2();\r
598 \r
599   /* Make the window visible; update its client area; and return "success" */\r
600   EnsureOnScreen(&boardX, &boardY);\r
601   wp.length = sizeof(WINDOWPLACEMENT);\r
602   wp.flags = 0;\r
603   wp.showCmd = nCmdShow;\r
604   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
605   wp.rcNormalPosition.left = boardX;\r
606   wp.rcNormalPosition.right = boardX + winWidth;\r
607   wp.rcNormalPosition.top = boardY;\r
608   wp.rcNormalPosition.bottom = boardY + winHeight;\r
609   SetWindowPlacement(hwndMain, &wp);\r
610 \r
611   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
612                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
613   if (hwndConsole) {\r
614 #if AOT_CONSOLE\r
615     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
616                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
617 #endif\r
618     ShowWindow(hwndConsole, nCmdShow);\r
619   }\r
620   UpdateWindow(hwnd);\r
621 \r
622   return TRUE;\r
623 \r
624 }\r
625 \r
626 \r
627 typedef enum {\r
628   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
629   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
630   ArgSettingsFilename\r
631 } ArgType;\r
632 \r
633 typedef struct {\r
634   char *argName;\r
635   ArgType argType;\r
636   /***\r
637   union {\r
638     String *pString;       // ArgString\r
639     int *pInt;             // ArgInt\r
640     float *pFloat;         // ArgFloat\r
641     Boolean *pBoolean;     // ArgBoolean\r
642     COLORREF *pColor;      // ArgColor\r
643     ColorClass cc;         // ArgAttribs\r
644     String *pFilename;     // ArgFilename\r
645     BoardSize *pBoardSize; // ArgBoardSize\r
646     int whichFont;         // ArgFont\r
647     DCB *pDCB;             // ArgCommSettings\r
648     String *pFilename;     // ArgSettingsFilename\r
649   } argLoc;\r
650   ***/\r
651   LPVOID argLoc;\r
652   BOOL save;\r
653 } ArgDescriptor;\r
654 \r
655 int junk;\r
656 ArgDescriptor argDescriptors[] = {\r
657   /* positional arguments */\r
658   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
659   { "", ArgNone, NULL },\r
660   /* keyword arguments */\r
661   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
662   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
663   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
664   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
665   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
666   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
667   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
668   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
669   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
670   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
671   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
672   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
673   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
674   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
675   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
676   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
677   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
678   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
679     FALSE },\r
680   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
681     FALSE },\r
682   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
683     FALSE },\r
684   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
685   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
686     FALSE },\r
687   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
688   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
689   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
690   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
691   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
692   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
693   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
694   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
695   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
696   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
697   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
698   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
699   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
700   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
701   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
702   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
703   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
704   /*!!bitmapDirectory?*/\r
705   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
706   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
707   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
708   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
709   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
710   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
711   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
712   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
713   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
714   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
715   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
716   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
717   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
718   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
719   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
720   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
721   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
722   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
723   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
724   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
725   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
726   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
727   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
728   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
729   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
730   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
731   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
732   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
733   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
734   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
735   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
736   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
737   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
738   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
739   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
740   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
741   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
742   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
743   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
744   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
745   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
746   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
747   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
748   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
749   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
750   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
751   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
752   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
753   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
754   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
755   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
756   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
757   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
758   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
759   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
760   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
761   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
762   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
763   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
764   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
765   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
766   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
767   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
768   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
769   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
770   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
771   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
772   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
773   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
774   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
775   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
776   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
777   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
778   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
779   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
780   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
781   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
782   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
783   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
784   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
785   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
786   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
787   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
788   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
789   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
790   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
791   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
792   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
793   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
794   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
795   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
796   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
797   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
798   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
799     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
800   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
801   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
802   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
803   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
804   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
805   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
806   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
807     TRUE }, /* must come after all fonts */\r
808   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
809   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
810     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
811   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
812   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
813   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
814   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
815   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
816   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
817   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
818   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
819   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
820   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
821   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
822   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
823   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
824   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
825   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
826   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
827   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
828   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
829   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
830   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
831   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
832   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
833   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
834   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
835   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
836   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
837   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
838   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
839 #if 0\r
840   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
841   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
842 #endif\r
843   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
844   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
845   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
846   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
847   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
848   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
849   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
850   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
851   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
852   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
853   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
854   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
855   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
856   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
857   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
858   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
859   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
860   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
861   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
862   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
863   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
864   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
865   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
866   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
867   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
868   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
869   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
870   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
871   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
872   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
873   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
874   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
875   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
876   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
877   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
878   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
879   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
880   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
881   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
882   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
883   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
884   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
885   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
886   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
887   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
888   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
889   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
890   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
891   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
892   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
893   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
894   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
895   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
896   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
897   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
898   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
899   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
900   { "highlightLastMove", ArgBoolean,\r
901     (LPVOID) &appData.highlightLastMove, TRUE },\r
902   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
903   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
904   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
905   { "highlightDragging", ArgBoolean,\r
906     (LPVOID) &appData.highlightDragging, TRUE },\r
907   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
908   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
909   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
910   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
911   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
912   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
913   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
914   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
915   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
916   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
917   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
918   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
919   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
920   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
921   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
922   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
923   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
924   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
925   { "soundShout", ArgFilename,\r
926     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
927   { "soundSShout", ArgFilename,\r
928     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
929   { "soundChannel1", ArgFilename,\r
930     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
931   { "soundChannel", ArgFilename,\r
932     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
933   { "soundKibitz", ArgFilename,\r
934     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
935   { "soundTell", ArgFilename,\r
936     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
937   { "soundChallenge", ArgFilename,\r
938     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
939   { "soundRequest", ArgFilename,\r
940     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
941   { "soundSeek", ArgFilename,\r
942     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
943   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
944   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
945   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
946   { "soundIcsLoss", ArgFilename, \r
947     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
948   { "soundIcsDraw", ArgFilename, \r
949     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
950   { "soundIcsUnfinished", ArgFilename, \r
951     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
952   { "soundIcsAlarm", ArgFilename, \r
953     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
954   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
955   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
956   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
957   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
958   { "reuseChessPrograms", ArgBoolean,\r
959     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
960   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
961   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
962   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
963   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
964   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
965   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
966   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
967   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
968   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
969   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
970   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
971   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
972   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
973   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
974   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
975   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
976   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
977   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
978   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
979   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
980   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
981   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
982   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
983   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
984   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
985   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
986   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
987   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
988   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
989   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
990   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
991   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
992   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
993   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
994   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
995   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
996   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
997     TRUE },\r
998   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
999     TRUE },\r
1000   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1001   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1002   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1003   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,\r
1004     FALSE },\r
1005   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,\r
1006     FALSE },\r
1007   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1008   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1009   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1010   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1011 #ifdef ZIPPY\r
1012   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1013   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1014   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1015   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1016   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1017   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1018   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1019   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1020   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1021   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1022   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1023   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1024   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1025     FALSE },\r
1026   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1027   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1028   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1029   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1030   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1031   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1032   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1033     FALSE },\r
1034   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1035   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1036   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1037   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1038   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1039   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1040   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1041   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1042   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1043   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1044   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1045   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1046   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1047   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1048   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1049   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1050   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1051   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1052 #endif\r
1053   { NULL, ArgNone, NULL, FALSE }\r
1054 };\r
1055 \r
1056 \r
1057 /* Kludge for indirection files on command line */\r
1058 char* lastIndirectionFilename;\r
1059 ArgDescriptor argDescriptorIndirection =\r
1060 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1061 \r
1062 \r
1063 VOID\r
1064 ExitArgError(char *msg, char *badArg)\r
1065 {\r
1066   char buf[MSG_SIZ];\r
1067 \r
1068   sprintf(buf, "%s %s", msg, badArg);\r
1069   DisplayFatalError(buf, 0, 2);\r
1070   exit(2);\r
1071 }\r
1072 \r
1073 /* Command line font name parser.  NULL name means do nothing.\r
1074    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1075    For backward compatibility, syntax without the colon is also\r
1076    accepted, but font names with digits in them won't work in that case.\r
1077 */\r
1078 VOID\r
1079 ParseFontName(char *name, MyFontParams *mfp)\r
1080 {\r
1081   char *p, *q;\r
1082   if (name == NULL) return;\r
1083   p = name;\r
1084   q = strchr(p, ':');\r
1085   if (q) {\r
1086     if (q - p >= sizeof(mfp->faceName))\r
1087       ExitArgError("Font name too long:", name);\r
1088     memcpy(mfp->faceName, p, q - p);\r
1089     mfp->faceName[q - p] = NULLCHAR;\r
1090     p = q + 1;\r
1091   } else {\r
1092     q = mfp->faceName;\r
1093     while (*p && !isdigit(*p)) {\r
1094       *q++ = *p++;\r
1095       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1096         ExitArgError("Font name too long:", name);\r
1097     }\r
1098     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1099     *q = NULLCHAR;\r
1100   }\r
1101   if (!*p) ExitArgError("Font point size missing:", name);\r
1102   mfp->pointSize = (float) atof(p);\r
1103   mfp->bold = (strchr(p, 'b') != NULL);\r
1104   mfp->italic = (strchr(p, 'i') != NULL);\r
1105   mfp->underline = (strchr(p, 'u') != NULL);\r
1106   mfp->strikeout = (strchr(p, 's') != NULL);\r
1107 }\r
1108 \r
1109 /* Color name parser.\r
1110    X version accepts X color names, but this one\r
1111    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1112 COLORREF\r
1113 ParseColorName(char *name)\r
1114 {\r
1115   int red, green, blue, count;\r
1116   char buf[MSG_SIZ];\r
1117 \r
1118   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1119   if (count != 3) {\r
1120     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1121       &red, &green, &blue);\r
1122   }\r
1123   if (count != 3) {\r
1124     sprintf(buf, "Can't parse color name %s", name);\r
1125     DisplayError(buf, 0);\r
1126     return RGB(0, 0, 0);\r
1127   }\r
1128   return PALETTERGB(red, green, blue);\r
1129 }\r
1130 \r
1131 \r
1132 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1133 {\r
1134   char *e = argValue;\r
1135   int eff = 0;\r
1136 \r
1137   while (*e) {\r
1138     if (*e == 'b')      eff |= CFE_BOLD;\r
1139     else if (*e == 'i') eff |= CFE_ITALIC;\r
1140     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1141     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1142     else if (*e == '#' || isdigit(*e)) break;\r
1143     e++;\r
1144   }\r
1145   *effects = eff;\r
1146   *color   = ParseColorName(e);\r
1147 }\r
1148 \r
1149 \r
1150 BoardSize\r
1151 ParseBoardSize(char *name)\r
1152 {\r
1153   BoardSize bs = SizeTiny;\r
1154   while (sizeInfo[bs].name != NULL) {\r
1155     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1156     bs++;\r
1157   }\r
1158   ExitArgError("Unrecognized board size value", name);\r
1159   return bs; /* not reached */\r
1160 }\r
1161 \r
1162 \r
1163 char\r
1164 StringGet(void *getClosure)\r
1165 {\r
1166   char **p = (char **) getClosure;\r
1167   return *((*p)++);\r
1168 }\r
1169 \r
1170 char\r
1171 FileGet(void *getClosure)\r
1172 {\r
1173   int c;\r
1174   FILE* f = (FILE*) getClosure;\r
1175 \r
1176   c = getc(f);\r
1177   if (c == EOF)\r
1178     return NULLCHAR;\r
1179   else\r
1180     return (char) c;\r
1181 }\r
1182 \r
1183 /* Parse settings file named "name". If file found, return the\r
1184    full name in fullname and return TRUE; else return FALSE */\r
1185 BOOLEAN\r
1186 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1187 {\r
1188   char *dummy;\r
1189   FILE *f;\r
1190 \r
1191   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1192     f = fopen(fullname, "r");\r
1193     if (f != NULL) {\r
1194       ParseArgs(FileGet, f);\r
1195       fclose(f);\r
1196       return TRUE;\r
1197     }\r
1198   }\r
1199   return FALSE;\r
1200 }\r
1201 \r
1202 VOID\r
1203 ParseArgs(GetFunc get, void *cl)\r
1204 {\r
1205   char argName[ARG_MAX];\r
1206   char argValue[ARG_MAX];\r
1207   ArgDescriptor *ad;\r
1208   char start;\r
1209   char *q;\r
1210   int i, octval;\r
1211   char ch;\r
1212   int posarg = 0;\r
1213 \r
1214   ch = get(cl);\r
1215   for (;;) {\r
1216     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1217     if (ch == NULLCHAR) break;\r
1218     if (ch == ';') {\r
1219       /* Comment to end of line */\r
1220       ch = get(cl);\r
1221       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1222       continue;\r
1223     } else if (ch == '/' || ch == '-') {\r
1224       /* Switch */\r
1225       q = argName;\r
1226       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1227              ch != '\n' && ch != '\t') {\r
1228         *q++ = ch;\r
1229         ch = get(cl);\r
1230       }\r
1231       *q = NULLCHAR;\r
1232 \r
1233       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1234         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1235 \r
1236       if (ad->argName == NULL)\r
1237         ExitArgError("Unrecognized argument", argName);\r
1238 \r
1239     } else if (ch == '@') {\r
1240       /* Indirection file */\r
1241       ad = &argDescriptorIndirection;\r
1242       ch = get(cl);\r
1243     } else {\r
1244       /* Positional argument */\r
1245       ad = &argDescriptors[posarg++];\r
1246       strcpy(argName, ad->argName);\r
1247     }\r
1248 \r
1249     if (ad->argType == ArgTrue) {\r
1250       *(Boolean *) ad->argLoc = TRUE;\r
1251       continue;\r
1252     }\r
1253     if (ad->argType == ArgFalse) {\r
1254       *(Boolean *) ad->argLoc = FALSE;\r
1255       continue;\r
1256     }\r
1257 \r
1258     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1259     if (ch == NULLCHAR || ch == '\n') {\r
1260       ExitArgError("No value provided for argument", argName);\r
1261     }\r
1262     q = argValue;\r
1263     if (ch == '{') {\r
1264       // Quoting with { }.  No characters have to (or can) be escaped.\r
1265       // Thus the string cannot contain a '}' character.\r
1266       start = ch;\r
1267       ch = get(cl);\r
1268       while (start) {\r
1269         switch (ch) {\r
1270         case NULLCHAR:\r
1271           start = NULLCHAR;\r
1272           break;\r
1273           \r
1274         case '}':\r
1275           ch = get(cl);\r
1276           start = NULLCHAR;\r
1277           break;\r
1278 \r
1279         default:\r
1280           *q++ = ch;\r
1281           ch = get(cl);\r
1282           break;\r
1283         }\r
1284       }   \r
1285     } else if (ch == '\'' || ch == '"') {\r
1286       // Quoting with ' ' or " ", with \ as escape character.\r
1287       // Inconvenient for long strings that may contain Windows filenames.\r
1288       start = ch;\r
1289       ch = get(cl);\r
1290       while (start) {\r
1291         switch (ch) {\r
1292         case NULLCHAR:\r
1293           start = NULLCHAR;\r
1294           break;\r
1295 \r
1296         default:\r
1297         not_special:\r
1298           *q++ = ch;\r
1299           ch = get(cl);\r
1300           break;\r
1301 \r
1302         case '\'':\r
1303         case '\"':\r
1304           if (ch == start) {\r
1305             ch = get(cl);\r
1306             start = NULLCHAR;\r
1307             break;\r
1308           } else {\r
1309             goto not_special;\r
1310           }\r
1311 \r
1312         case '\\':\r
1313           if (ad->argType == ArgFilename\r
1314               || ad->argType == ArgSettingsFilename) {\r
1315               goto not_special;\r
1316           }\r
1317           ch = get(cl);\r
1318           switch (ch) {\r
1319           case NULLCHAR:\r
1320             ExitArgError("Incomplete \\ escape in value for", argName);\r
1321             break;\r
1322           case 'n':\r
1323             *q++ = '\n';\r
1324             ch = get(cl);\r
1325             break;\r
1326           case 'r':\r
1327             *q++ = '\r';\r
1328             ch = get(cl);\r
1329             break;\r
1330           case 't':\r
1331             *q++ = '\t';\r
1332             ch = get(cl);\r
1333             break;\r
1334           case 'b':\r
1335             *q++ = '\b';\r
1336             ch = get(cl);\r
1337             break;\r
1338           case 'f':\r
1339             *q++ = '\f';\r
1340             ch = get(cl);\r
1341             break;\r
1342           default:\r
1343             octval = 0;\r
1344             for (i = 0; i < 3; i++) {\r
1345               if (ch >= '0' && ch <= '7') {\r
1346                 octval = octval*8 + (ch - '0');\r
1347                 ch = get(cl);\r
1348               } else {\r
1349                 break;\r
1350               }\r
1351             }\r
1352             if (i > 0) {\r
1353               *q++ = (char) octval;\r
1354             } else {\r
1355               *q++ = ch;\r
1356               ch = get(cl);\r
1357             }\r
1358             break;\r
1359           }\r
1360           break;\r
1361         }\r
1362       }\r
1363     } else {\r
1364       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1365         *q++ = ch;\r
1366         ch = get(cl);\r
1367       }\r
1368     }\r
1369     *q = NULLCHAR;\r
1370 \r
1371     switch (ad->argType) {\r
1372     case ArgInt:\r
1373       *(int *) ad->argLoc = atoi(argValue);\r
1374       break;\r
1375 \r
1376     case ArgFloat:\r
1377       *(float *) ad->argLoc = (float) atof(argValue);\r
1378       break;\r
1379 \r
1380     case ArgString:\r
1381     case ArgFilename:\r
1382       *(char **) ad->argLoc = strdup(argValue);\r
1383       break;\r
1384 \r
1385     case ArgSettingsFilename:\r
1386       {\r
1387         char fullname[MSG_SIZ];\r
1388         if (ParseSettingsFile(argValue, fullname)) {\r
1389           if (ad->argLoc != NULL) {\r
1390             *(char **) ad->argLoc = strdup(fullname);\r
1391           }\r
1392         } else {\r
1393           if (ad->argLoc != NULL) {\r
1394           } else {\r
1395             ExitArgError("Failed to open indirection file", argValue);\r
1396           }\r
1397         }\r
1398       }\r
1399       break;\r
1400 \r
1401     case ArgBoolean:\r
1402       switch (argValue[0]) {\r
1403       case 't':\r
1404       case 'T':\r
1405         *(Boolean *) ad->argLoc = TRUE;\r
1406         break;\r
1407       case 'f':\r
1408       case 'F':\r
1409         *(Boolean *) ad->argLoc = FALSE;\r
1410         break;\r
1411       default:\r
1412         ExitArgError("Unrecognized boolean argument value", argValue);\r
1413         break;\r
1414       }\r
1415       break;\r
1416 \r
1417     case ArgColor:\r
1418       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1419       break;\r
1420 \r
1421     case ArgAttribs: {\r
1422       ColorClass cc = (ColorClass)ad->argLoc;\r
1423       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1424       }\r
1425       break;\r
1426       \r
1427     case ArgBoardSize:\r
1428       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1429       break;\r
1430 \r
1431     case ArgFont:\r
1432       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1433       break;\r
1434 \r
1435     case ArgCommSettings:\r
1436       ParseCommSettings(argValue, &dcb);\r
1437       break;\r
1438 \r
1439     case ArgNone:\r
1440       ExitArgError("Unrecognized argument", argValue);\r
1441       break;\r
1442     }\r
1443   }\r
1444 }\r
1445 \r
1446 VOID\r
1447 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1448 {\r
1449   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1450   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1451   DeleteDC(hdc);\r
1452   lf->lfWidth = 0;\r
1453   lf->lfEscapement = 0;\r
1454   lf->lfOrientation = 0;\r
1455   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1456   lf->lfItalic = mfp->italic;\r
1457   lf->lfUnderline = mfp->underline;\r
1458   lf->lfStrikeOut = mfp->strikeout;\r
1459   lf->lfCharSet = DEFAULT_CHARSET;\r
1460   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1461   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1462   lf->lfQuality = DEFAULT_QUALITY;\r
1463   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1464   strcpy(lf->lfFaceName, mfp->faceName);\r
1465 }\r
1466 \r
1467 VOID\r
1468 CreateFontInMF(MyFont *mf)\r
1469 {\r
1470   LFfromMFP(&mf->lf, &mf->mfp);\r
1471   if (mf->hf) DeleteObject(mf->hf);\r
1472   mf->hf = CreateFontIndirect(&mf->lf);\r
1473 }\r
1474 \r
1475 VOID\r
1476 SetDefaultTextAttribs()\r
1477 {\r
1478   ColorClass cc;\r
1479   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1480     ParseAttribs(&textAttribs[cc].color, \r
1481                  &textAttribs[cc].effects, \r
1482                  defaultTextAttribs[cc]);\r
1483   }\r
1484 }\r
1485 \r
1486 VOID\r
1487 SetDefaultSounds()\r
1488 {\r
1489   ColorClass cc;\r
1490   SoundClass sc;\r
1491   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1492     textAttribs[cc].sound.name = strdup("");\r
1493     textAttribs[cc].sound.data = NULL;\r
1494   }\r
1495   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1496     sounds[sc].name = strdup("");\r
1497     sounds[sc].data = NULL;\r
1498   }\r
1499   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1500 }\r
1501 \r
1502 VOID\r
1503 LoadAllSounds()\r
1504 {\r
1505   ColorClass cc;\r
1506   SoundClass sc;\r
1507   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1508     MyLoadSound(&textAttribs[cc].sound);\r
1509   }\r
1510   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1511     MyLoadSound(&sounds[sc]);\r
1512   }\r
1513 }\r
1514 \r
1515 VOID\r
1516 InitAppData(LPSTR lpCmdLine)\r
1517 {\r
1518   int i, j;\r
1519   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1520   char *dummy, *p;\r
1521 \r
1522   programName = szAppName;\r
1523 \r
1524   /* Initialize to defaults */\r
1525   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1526   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1527   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1528   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1529   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1530   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1531   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1532   SetDefaultTextAttribs();\r
1533   SetDefaultSounds();\r
1534   appData.movesPerSession = MOVES_PER_SESSION;\r
1535   appData.initString = INIT_STRING;\r
1536   appData.secondInitString = INIT_STRING;\r
1537   appData.firstComputerString = COMPUTER_STRING;\r
1538   appData.secondComputerString = COMPUTER_STRING;\r
1539   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1540   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1541   appData.firstPlaysBlack = FALSE;\r
1542   appData.noChessProgram = FALSE;\r
1543   chessProgram = FALSE;\r
1544   appData.firstHost = FIRST_HOST;\r
1545   appData.secondHost = SECOND_HOST;\r
1546   appData.firstDirectory = FIRST_DIRECTORY;\r
1547   appData.secondDirectory = SECOND_DIRECTORY;\r
1548   appData.bitmapDirectory = "";\r
1549   appData.remoteShell = REMOTE_SHELL;\r
1550   appData.remoteUser = "";\r
1551   appData.timeDelay = TIME_DELAY;\r
1552   appData.timeControl = TIME_CONTROL;\r
1553   appData.timeIncrement = TIME_INCREMENT;\r
1554   appData.icsActive = FALSE;\r
1555   appData.icsHost = "";\r
1556   appData.icsPort = ICS_PORT;\r
1557   appData.icsCommPort = ICS_COMM_PORT;\r
1558   appData.icsLogon = ICS_LOGON;\r
1559   appData.icsHelper = "";\r
1560   appData.useTelnet = FALSE;\r
1561   appData.telnetProgram = TELNET_PROGRAM;\r
1562   appData.gateway = "";\r
1563   appData.loadGameFile = "";\r
1564   appData.loadGameIndex = 0;\r
1565   appData.saveGameFile = "";\r
1566   appData.autoSaveGames = FALSE;\r
1567   appData.loadPositionFile = "";\r
1568   appData.loadPositionIndex = 1;\r
1569   appData.savePositionFile = "";\r
1570   appData.matchMode = FALSE;\r
1571   appData.matchGames = 0;\r
1572   appData.monoMode = FALSE;\r
1573   appData.debugMode = FALSE;\r
1574   appData.clockMode = TRUE;\r
1575   boardSize = (BoardSize) -1; /* determine by screen size */\r
1576   appData.Iconic = FALSE; /*unused*/\r
1577   appData.searchTime = "";\r
1578   appData.searchDepth = 0;\r
1579   appData.showCoords = FALSE;\r
1580   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1581   appData.autoCallFlag = FALSE;\r
1582   appData.flipView = FALSE;\r
1583   appData.autoFlipView = TRUE;\r
1584   appData.cmailGameName = "";\r
1585   appData.alwaysPromoteToQueen = FALSE;\r
1586   appData.oldSaveStyle = FALSE;\r
1587   appData.quietPlay = FALSE;\r
1588   appData.showThinking = FALSE;\r
1589   appData.ponderNextMove = TRUE;\r
1590   appData.periodicUpdates = TRUE;\r
1591   appData.popupExitMessage = TRUE;\r
1592   appData.popupMoveErrors = FALSE;\r
1593   appData.autoObserve = FALSE;\r
1594   appData.autoComment = FALSE;\r
1595   appData.animate = TRUE;\r
1596   appData.animSpeed = 10;\r
1597   appData.animateDragging = TRUE;\r
1598   appData.highlightLastMove = TRUE;\r
1599   appData.getMoveList = TRUE;\r
1600   appData.testLegality = TRUE;\r
1601   appData.premove = TRUE;\r
1602   appData.premoveWhite = FALSE;\r
1603   appData.premoveWhiteText = "";\r
1604   appData.premoveBlack = FALSE;\r
1605   appData.premoveBlackText = "";\r
1606   appData.icsAlarm = TRUE;\r
1607   appData.icsAlarmTime = 5000;\r
1608   appData.autoRaiseBoard = TRUE;\r
1609   appData.localLineEditing = TRUE;\r
1610   appData.colorize = TRUE;\r
1611   appData.reuseFirst = TRUE;\r
1612   appData.reuseSecond = TRUE;\r
1613   appData.blindfold = FALSE;\r
1614   dcb.DCBlength = sizeof(DCB);\r
1615   dcb.BaudRate = 9600;\r
1616   dcb.fBinary = TRUE;\r
1617   dcb.fParity = FALSE;\r
1618   dcb.fOutxCtsFlow = FALSE;\r
1619   dcb.fOutxDsrFlow = FALSE;\r
1620   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1621   dcb.fDsrSensitivity = FALSE;\r
1622   dcb.fTXContinueOnXoff = TRUE;\r
1623   dcb.fOutX = FALSE;\r
1624   dcb.fInX = FALSE;\r
1625   dcb.fNull = FALSE;\r
1626   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1627   dcb.fAbortOnError = FALSE;\r
1628   dcb.wReserved = 0;\r
1629   dcb.ByteSize = 7;\r
1630   dcb.Parity = SPACEPARITY;\r
1631   dcb.StopBits = ONESTOPBIT;\r
1632   settingsFileName = SETTINGS_FILE;\r
1633   saveSettingsOnExit = TRUE;\r
1634   boardX = CW_USEDEFAULT;\r
1635   boardY = CW_USEDEFAULT;\r
1636   consoleX = CW_USEDEFAULT; \r
1637   consoleY = CW_USEDEFAULT; \r
1638   consoleW = CW_USEDEFAULT;\r
1639   consoleH = CW_USEDEFAULT;\r
1640   analysisX = CW_USEDEFAULT; \r
1641   analysisY = CW_USEDEFAULT; \r
1642   analysisW = CW_USEDEFAULT;\r
1643   analysisH = CW_USEDEFAULT;\r
1644   commentX = CW_USEDEFAULT; \r
1645   commentY = CW_USEDEFAULT; \r
1646   commentW = CW_USEDEFAULT;\r
1647   commentH = CW_USEDEFAULT;\r
1648   editTagsX = CW_USEDEFAULT; \r
1649   editTagsY = CW_USEDEFAULT; \r
1650   editTagsW = CW_USEDEFAULT;\r
1651   editTagsH = CW_USEDEFAULT;\r
1652   gameListX = CW_USEDEFAULT; \r
1653   gameListY = CW_USEDEFAULT; \r
1654   gameListW = CW_USEDEFAULT;\r
1655   gameListH = CW_USEDEFAULT;\r
1656   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1657   icsNames = ICS_NAMES;\r
1658   firstChessProgramNames = FCP_NAMES;\r
1659   secondChessProgramNames = SCP_NAMES;\r
1660   appData.initialMode = "";\r
1661   appData.variant = "normal";\r
1662   appData.firstProtocolVersion = PROTOVER;\r
1663   appData.secondProtocolVersion = PROTOVER;\r
1664   appData.showButtonBar = TRUE;\r
1665 #ifdef ZIPPY\r
1666   appData.zippyTalk = ZIPPY_TALK;\r
1667   appData.zippyPlay = ZIPPY_PLAY;\r
1668   appData.zippyLines = ZIPPY_LINES;\r
1669   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1670   appData.zippyPassword = ZIPPY_PASSWORD;\r
1671   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1672   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1673   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1674   appData.zippyUseI = ZIPPY_USE_I;\r
1675   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1676   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1677   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1678   appData.zippyGameStart = ZIPPY_GAME_START;\r
1679   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1680   appData.zippyAbort = ZIPPY_ABORT;\r
1681   appData.zippyVariants = ZIPPY_VARIANTS;\r
1682   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1683   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1684 #endif\r
1685 \r
1686   /* Point font array elements to structures and\r
1687      parse default font names */\r
1688   for (i=0; i<NUM_FONTS; i++) {\r
1689     for (j=0; j<NUM_SIZES; j++) {\r
1690       font[j][i] = &fontRec[j][i];\r
1691       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1692     }\r
1693   }\r
1694   \r
1695   /* Parse default settings file if any */\r
1696   if (ParseSettingsFile(settingsFileName, buf)) {\r
1697     settingsFileName = strdup(buf);\r
1698   }\r
1699 \r
1700   /* Parse command line */\r
1701   ParseArgs(StringGet, &lpCmdLine);\r
1702 \r
1703   /* Propagate options that affect others */\r
1704   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1705   if (appData.icsActive || appData.noChessProgram) {\r
1706      chessProgram = FALSE;  /* not local chess program mode */\r
1707   }\r
1708 \r
1709   /* Open startup dialog if needed */\r
1710   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1711       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1712       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1713                         *appData.secondChessProgram == NULLCHAR))) {\r
1714     FARPROC lpProc;\r
1715     \r
1716     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1717     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1718     FreeProcInstance(lpProc);\r
1719   }\r
1720 \r
1721   /* Make sure save files land in the right (?) directory */\r
1722   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
1723     appData.saveGameFile = strdup(buf);\r
1724   }\r
1725   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1726     appData.savePositionFile = strdup(buf);\r
1727   }\r
1728 \r
1729   /* Finish initialization for fonts and sounds */\r
1730   for (i=0; i<NUM_FONTS; i++) {\r
1731     for (j=0; j<NUM_SIZES; j++) {\r
1732       CreateFontInMF(font[j][i]);\r
1733     }\r
1734   }\r
1735   /* xboard, and older WinBoards, controlled the move sound with the\r
1736      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1737      always turn the option on (so that the backend will call us),\r
1738      then let the user turn the sound off by setting it to silence if\r
1739      desired.  To accommodate old winboard.ini files saved by old\r
1740      versions of WinBoard, we also turn off the sound if the option\r
1741      was initially set to false. */\r
1742   if (!appData.ringBellAfterMoves) {\r
1743     sounds[(int)SoundMove].name = strdup("");\r
1744     appData.ringBellAfterMoves = TRUE;\r
1745   }\r
1746   GetCurrentDirectory(MSG_SIZ, currDir);\r
1747   SetCurrentDirectory(installDir);\r
1748   LoadAllSounds();\r
1749   SetCurrentDirectory(currDir);\r
1750 \r
1751   p = icsTextMenuString;\r
1752   if (p[0] == '@') {\r
1753     FILE* f = fopen(p + 1, "r");\r
1754     if (f == NULL) {\r
1755       DisplayFatalError(p + 1, errno, 2);\r
1756       return;\r
1757     }\r
1758     i = fread(buf, 1, sizeof(buf)-1, f);\r
1759     fclose(f);\r
1760     buf[i] = NULLCHAR;\r
1761     p = buf;\r
1762   }\r
1763   ParseIcsTextMenu(strdup(p));\r
1764 }\r
1765 \r
1766 \r
1767 VOID\r
1768 InitMenuChecks()\r
1769 {\r
1770   HMENU hmenu = GetMenu(hwndMain);\r
1771 \r
1772   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1773                         MF_BYCOMMAND|((appData.icsActive &&\r
1774                                        *appData.icsCommPort != NULLCHAR) ?\r
1775                                       MF_ENABLED : MF_GRAYED));\r
1776   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1777                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1778                                      MF_CHECKED : MF_UNCHECKED));\r
1779 }\r
1780 \r
1781 \r
1782 VOID\r
1783 SaveSettings(char* name)\r
1784 {\r
1785   FILE *f;\r
1786   ArgDescriptor *ad;\r
1787   WINDOWPLACEMENT wp;\r
1788   char dir[MSG_SIZ];\r
1789 \r
1790   if (!hwndMain) return;\r
1791 \r
1792   GetCurrentDirectory(MSG_SIZ, dir);\r
1793   SetCurrentDirectory(installDir);\r
1794   f = fopen(name, "w");\r
1795   SetCurrentDirectory(dir);\r
1796   if (f == NULL) {\r
1797     DisplayError(name, errno);\r
1798     return;\r
1799   }\r
1800   fprintf(f, ";\n");\r
1801   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
1802   fprintf(f, ";\n");\r
1803   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
1804   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
1805   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
1806   fprintf(f, ";\n");\r
1807 \r
1808   wp.length = sizeof(WINDOWPLACEMENT);\r
1809   GetWindowPlacement(hwndMain, &wp);\r
1810   boardX = wp.rcNormalPosition.left;\r
1811   boardY = wp.rcNormalPosition.top;\r
1812 \r
1813   if (hwndConsole) {\r
1814     GetWindowPlacement(hwndConsole, &wp);\r
1815     consoleX = wp.rcNormalPosition.left;\r
1816     consoleY = wp.rcNormalPosition.top;\r
1817     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1818     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1819   }\r
1820 \r
1821   if (analysisDialog) {\r
1822     GetWindowPlacement(analysisDialog, &wp);\r
1823     analysisX = wp.rcNormalPosition.left;\r
1824     analysisY = wp.rcNormalPosition.top;\r
1825     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1826     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1827   }\r
1828 \r
1829   if (commentDialog) {\r
1830     GetWindowPlacement(commentDialog, &wp);\r
1831     commentX = wp.rcNormalPosition.left;\r
1832     commentY = wp.rcNormalPosition.top;\r
1833     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1834     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1835   }\r
1836 \r
1837   if (editTagsDialog) {\r
1838     GetWindowPlacement(editTagsDialog, &wp);\r
1839     editTagsX = wp.rcNormalPosition.left;\r
1840     editTagsY = wp.rcNormalPosition.top;\r
1841     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1842     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1843   }\r
1844 \r
1845   if (gameListDialog) {\r
1846     GetWindowPlacement(gameListDialog, &wp);\r
1847     gameListX = wp.rcNormalPosition.left;\r
1848     gameListY = wp.rcNormalPosition.top;\r
1849     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1850     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1851   }\r
1852 \r
1853   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
1854     if (!ad->save) continue;\r
1855     switch (ad->argType) {\r
1856     case ArgString:\r
1857       {\r
1858         char *p = *(char **)ad->argLoc;\r
1859         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
1860           /* Quote multiline values or \-containing values\r
1861              with { } if possible */\r
1862           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
1863         } else {\r
1864           /* Else quote with " " */\r
1865           fprintf(f, "/%s=\"", ad->argName);\r
1866           while (*p) {\r
1867             if (*p == '\n') fprintf(f, "\n");\r
1868             else if (*p == '\r') fprintf(f, "\\r");\r
1869             else if (*p == '\t') fprintf(f, "\\t");\r
1870             else if (*p == '\b') fprintf(f, "\\b");\r
1871             else if (*p == '\f') fprintf(f, "\\f");\r
1872             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
1873             else if (*p == '\"') fprintf(f, "\\\"");\r
1874             else if (*p == '\\') fprintf(f, "\\\\");\r
1875             else putc(*p, f);\r
1876             p++;\r
1877           }\r
1878           fprintf(f, "\"\n");\r
1879         }\r
1880       }\r
1881       break;\r
1882     case ArgInt:\r
1883       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
1884       break;\r
1885     case ArgFloat:\r
1886       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
1887       break;\r
1888     case ArgBoolean:\r
1889       fprintf(f, "/%s=%s\n", ad->argName, \r
1890         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
1891       break;\r
1892     case ArgTrue:\r
1893       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
1894       break;\r
1895     case ArgFalse:\r
1896       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
1897       break;\r
1898     case ArgColor:\r
1899       {\r
1900         COLORREF color = *(COLORREF *)ad->argLoc;\r
1901         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
1902           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1903       }\r
1904       break;\r
1905     case ArgAttribs:\r
1906       {\r
1907         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1908         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
1909           (ta->effects & CFE_BOLD) ? "b" : "",\r
1910           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1911           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1912           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1913           (ta->effects) ? " " : "",\r
1914           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1915       }\r
1916       break;\r
1917     case ArgFilename:\r
1918       if (strchr(*(char **)ad->argLoc, '\"')) {\r
1919         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
1920       } else {\r
1921         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
1922       }\r
1923       break;\r
1924     case ArgBoardSize:\r
1925       fprintf(f, "/%s=%s\n", ad->argName,\r
1926               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
1927       break;\r
1928     case ArgFont:\r
1929       {\r
1930         int bs;\r
1931         for (bs=0; bs<NUM_SIZES; bs++) {\r
1932           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1933           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1934           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
1935             ad->argName, mfp->faceName, mfp->pointSize,\r
1936             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1937             mfp->bold ? "b" : "",\r
1938             mfp->italic ? "i" : "",\r
1939             mfp->underline ? "u" : "",\r
1940             mfp->strikeout ? "s" : "");\r
1941         }\r
1942       }\r
1943       break;\r
1944     case ArgCommSettings:\r
1945       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
1946     }\r
1947   }\r
1948   fclose(f);\r
1949 }\r
1950 \r
1951 \r
1952 \r
1953 /*---------------------------------------------------------------------------*\\r
1954  *\r
1955  * GDI board drawing routines\r
1956  *\r
1957 \*---------------------------------------------------------------------------*/\r
1958 \r
1959 HBITMAP\r
1960 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
1961 {\r
1962   char name[128];\r
1963 \r
1964   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
1965   if (gameInfo.event &&\r
1966       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
1967       strcmp(name, "k80s") == 0) {\r
1968     strcpy(name, "tim");\r
1969   }\r
1970   return LoadBitmap(hinst, name);\r
1971 }\r
1972 \r
1973 \r
1974 /* Insert a color into the program's logical palette\r
1975    structure.  This code assumes the given color is\r
1976    the result of the RGB or PALETTERGB macro, and it\r
1977    knows how those macros work (which is documented).\r
1978 */\r
1979 VOID\r
1980 InsertInPalette(COLORREF color)\r
1981 {\r
1982   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
1983 \r
1984   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
1985     DisplayFatalError("Too many colors", 0, 1);\r
1986     pLogPal->palNumEntries--;\r
1987     return;\r
1988   }\r
1989 \r
1990   pe->peFlags = (char) 0;\r
1991   pe->peRed = (char) (0xFF & color);\r
1992   pe->peGreen = (char) (0xFF & (color >> 8));\r
1993   pe->peBlue = (char) (0xFF & (color >> 16));\r
1994   return;\r
1995 }\r
1996 \r
1997 \r
1998 VOID\r
1999 InitDrawingColors()\r
2000 {\r
2001   if (pLogPal == NULL) {\r
2002     /* Allocate enough memory for a logical palette with\r
2003      * PALETTESIZE entries and set the size and version fields\r
2004      * of the logical palette structure.\r
2005      */\r
2006     pLogPal = (NPLOGPALETTE)\r
2007       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2008                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2009     pLogPal->palVersion    = 0x300;\r
2010   }\r
2011   pLogPal->palNumEntries = 0;\r
2012 \r
2013   InsertInPalette(lightSquareColor);\r
2014   InsertInPalette(darkSquareColor);\r
2015   InsertInPalette(whitePieceColor);\r
2016   InsertInPalette(blackPieceColor);\r
2017   InsertInPalette(highlightSquareColor);\r
2018   InsertInPalette(premoveHighlightColor);\r
2019 \r
2020   /*  create a logical color palette according the information\r
2021    *  in the LOGPALETTE structure.\r
2022    */\r
2023   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2024 \r
2025   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2026   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2027   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2028   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2029   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2030 }\r
2031 \r
2032 \r
2033 int\r
2034 BoardWidth(int boardSize)\r
2035 {\r
2036   return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +\r
2037           BOARD_SIZE * sizeInfo[boardSize].squareSize;\r
2038 }\r
2039 \r
2040 /* Respond to board resize by dragging edge */\r
2041 VOID\r
2042 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2043 {\r
2044   BoardSize newSize = NUM_SIZES - 1;\r
2045   static int recurse = 0;\r
2046   if (IsIconic(hwndMain)) return;\r
2047   if (recurse > 0) return;\r
2048   recurse++;\r
2049   while (newSize > 0 &&\r
2050          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2051           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2052     newSize--;\r
2053   } \r
2054   boardSize = newSize;\r
2055   InitDrawingSizes(boardSize, flags);\r
2056   recurse--;\r
2057 }\r
2058 \r
2059 \r
2060 \r
2061 VOID\r
2062 InitDrawingSizes(BoardSize boardSize, int flags)\r
2063 {\r
2064   int i, boardWidth;\r
2065   ChessSquare piece;\r
2066   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2067   HDC hdc;\r
2068   SIZE clockSize, messageSize;\r
2069   HFONT oldFont;\r
2070   char buf[MSG_SIZ];\r
2071   char *str;\r
2072   HMENU hmenu = GetMenu(hwndMain);\r
2073   RECT crect, wrect;\r
2074   int offby;\r
2075   LOGBRUSH logbrush;\r
2076 \r
2077   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2078   smallLayout = sizeInfo[boardSize].smallLayout;\r
2079   squareSize = sizeInfo[boardSize].squareSize;\r
2080   lineGap = sizeInfo[boardSize].lineGap;\r
2081 \r
2082   if (tinyLayout != oldTinyLayout) {\r
2083     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2084     if (tinyLayout) {\r
2085       style &= ~WS_SYSMENU;\r
2086       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2087                  "&Minimize\tCtrl+F4");\r
2088     } else {\r
2089       style |= WS_SYSMENU;\r
2090       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2091     }\r
2092     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2093 \r
2094     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2095       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2096         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2097     }\r
2098     DrawMenuBar(hwndMain);\r
2099   }\r
2100 \r
2101   boardWidth = BoardWidth(boardSize);\r
2102 \r
2103   /* Get text area sizes */\r
2104   hdc = GetDC(hwndMain);\r
2105   if (appData.clockMode) {\r
2106     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2107   } else {\r
2108     sprintf(buf, "White");\r
2109   }\r
2110   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2111   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2112   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2113   str = "We only care about the height here";\r
2114   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2115   SelectObject(hdc, oldFont);\r
2116   ReleaseDC(hwndMain, hdc);\r
2117 \r
2118   /* Compute where everything goes */\r
2119   whiteRect.left = OUTER_MARGIN;\r
2120   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2121   whiteRect.top = OUTER_MARGIN;\r
2122   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2123 \r
2124   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2125   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2126   blackRect.top = whiteRect.top;\r
2127   blackRect.bottom = whiteRect.bottom;\r
2128 \r
2129   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2130   if (appData.showButtonBar) {\r
2131     messageRect.right = blackRect.right\r
2132       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2133   } else {\r
2134     messageRect.right = blackRect.right;\r
2135   }\r
2136   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2137   messageRect.bottom = messageRect.top + messageSize.cy;\r
2138 \r
2139   boardRect.left = whiteRect.left;\r
2140   boardRect.right = boardRect.left + boardWidth;\r
2141   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2142   boardRect.bottom = boardRect.top + boardWidth;\r
2143 \r
2144   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2145   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2146   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2147   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2148     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2149   GetWindowRect(hwndMain, &wrect);\r
2150   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2151                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2152   /* compensate if menu bar wrapped */\r
2153   GetClientRect(hwndMain, &crect);\r
2154   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2155   winHeight += offby;\r
2156   switch (flags) {\r
2157   case WMSZ_TOPLEFT:\r
2158     SetWindowPos(hwndMain, NULL, \r
2159                  wrect.right - winWidth, wrect.bottom - winHeight, \r
2160                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2161     break;\r
2162 \r
2163   case WMSZ_TOPRIGHT:\r
2164   case WMSZ_TOP:\r
2165     SetWindowPos(hwndMain, NULL, \r
2166                  wrect.left, wrect.bottom - winHeight, \r
2167                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2168     break;\r
2169 \r
2170   case WMSZ_BOTTOMLEFT:\r
2171   case WMSZ_LEFT:\r
2172     SetWindowPos(hwndMain, NULL, \r
2173                  wrect.right - winWidth, wrect.top, \r
2174                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2175     break;\r
2176 \r
2177   case WMSZ_BOTTOMRIGHT:\r
2178   case WMSZ_BOTTOM:\r
2179   case WMSZ_RIGHT:\r
2180   default:\r
2181     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2182                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2183     break;\r
2184   }\r
2185 \r
2186   hwndPause = NULL;\r
2187   for (i = 0; i < N_BUTTONS; i++) {\r
2188     if (buttonDesc[i].hwnd != NULL) {\r
2189       DestroyWindow(buttonDesc[i].hwnd);\r
2190       buttonDesc[i].hwnd = NULL;\r
2191     }\r
2192     if (appData.showButtonBar) {\r
2193       buttonDesc[i].hwnd =\r
2194         CreateWindow("BUTTON", buttonDesc[i].label,\r
2195                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2196                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2197                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2198                      (HMENU) buttonDesc[i].id,\r
2199                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2200       if (tinyLayout) {\r
2201         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2202                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2203                     MAKELPARAM(FALSE, 0));\r
2204       }\r
2205       if (buttonDesc[i].id == IDM_Pause)\r
2206         hwndPause = buttonDesc[i].hwnd;\r
2207       buttonDesc[i].wndproc = (WNDPROC)\r
2208         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2209     }\r
2210   }\r
2211   if (gridPen != NULL) DeleteObject(gridPen);\r
2212   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2213   if (premovePen != NULL) DeleteObject(premovePen);\r
2214   if (lineGap != 0) {\r
2215     logbrush.lbStyle = BS_SOLID;\r
2216     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2217     gridPen =\r
2218       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2219                    lineGap, &logbrush, 0, NULL);\r
2220     logbrush.lbColor = highlightSquareColor;\r
2221     highlightPen =\r
2222       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2223                    lineGap, &logbrush, 0, NULL);\r
2224 \r
2225     logbrush.lbColor = premoveHighlightColor; \r
2226     premovePen =\r
2227       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2228                    lineGap, &logbrush, 0, NULL);\r
2229 \r
2230     for (i = 0; i < BOARD_SIZE + 1; i++) {\r
2231       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2232       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;\r
2233       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2234         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2235       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2236         BOARD_SIZE * (squareSize + lineGap);\r
2237       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =\r
2238         gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +\r
2239         lineGap / 2 + (i * (squareSize + lineGap));\r
2240       gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =\r
2241         boardRect.top + BOARD_SIZE * (squareSize + lineGap);\r
2242       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2243     }\r
2244   }\r
2245 \r
2246   if (boardSize == oldBoardSize) return;\r
2247   oldBoardSize = boardSize;\r
2248   oldTinyLayout = tinyLayout;\r
2249 \r
2250   /* Load piece bitmaps for this board size */\r
2251   for (i=0; i<=2; i++) {\r
2252     for (piece = WhitePawn;\r
2253          (int) piece <= (int) WhiteKing;\r
2254          piece = (ChessSquare) ((int) piece + 1)) {\r
2255       if (pieceBitmap[i][piece] != NULL)\r
2256         DeleteObject(pieceBitmap[i][piece]);\r
2257     }\r
2258   }\r
2259 \r
2260   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2261   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2262   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2263   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2264   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2265   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2266   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2267   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2268   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2269   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2270   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2271   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2272   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2273   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2274   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2275   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2276   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2277   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2278 \r
2279 }\r
2280 \r
2281 HBITMAP\r
2282 PieceBitmap(ChessSquare p, int kind)\r
2283 {\r
2284   if ((int) p >= (int) BlackPawn)\r
2285     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2286 \r
2287   return pieceBitmap[kind][(int) p];\r
2288 }\r
2289 \r
2290 /***************************************************************/\r
2291 \r
2292 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2293 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2294 /*\r
2295 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2296 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2297 */\r
2298 \r
2299 VOID\r
2300 SquareToPos(int row, int column, int * x, int * y)\r
2301 {\r
2302   if (flipView) {\r
2303     *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
2304     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2305   } else {\r
2306     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2307     *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
2308   }\r
2309 }\r
2310 \r
2311 VOID\r
2312 DrawCoordsOnDC(HDC hdc)\r
2313 {\r
2314   static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};\r
2315   static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};\r
2316   char str[2] = { NULLCHAR, NULLCHAR };\r
2317   int oldMode, oldAlign, x, y, start, i;\r
2318   HFONT oldFont;\r
2319   HBRUSH oldBrush;\r
2320 \r
2321   if (!appData.showCoords)\r
2322     return;\r
2323 \r
2324   start = flipView ? 0 : 8;\r
2325 \r
2326   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2327   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2328   oldAlign = GetTextAlign(hdc);\r
2329   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2330 \r
2331   y = boardRect.top + lineGap;\r
2332   x = boardRect.left + lineGap;\r
2333 \r
2334   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2335   for (i = 0; i < 8; i++) {\r
2336     str[0] = files[start + i];\r
2337     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2338     y += squareSize + lineGap;\r
2339   }\r
2340 \r
2341   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2342   for (i = 0; i < 8; i++) {\r
2343     str[0] = ranks[start + i];\r
2344     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2345     x += squareSize + lineGap;\r
2346   }    \r
2347 \r
2348   SelectObject(hdc, oldBrush);\r
2349   SetBkMode(hdc, oldMode);\r
2350   SetTextAlign(hdc, oldAlign);\r
2351   SelectObject(hdc, oldFont);\r
2352 }\r
2353 \r
2354 VOID\r
2355 DrawGridOnDC(HDC hdc)\r
2356 {\r
2357   HPEN oldPen;\r
2358  \r
2359   if (lineGap != 0) {\r
2360     oldPen = SelectObject(hdc, gridPen);\r
2361     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);\r
2362     SelectObject(hdc, oldPen);\r
2363   }\r
2364 }\r
2365 \r
2366 #define HIGHLIGHT_PEN 0\r
2367 #define PREMOVE_PEN   1\r
2368 \r
2369 VOID\r
2370 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2371 {\r
2372   int x1, y1;\r
2373   HPEN oldPen, hPen;\r
2374   if (lineGap == 0) return;\r
2375   if (flipView) {\r
2376     x1 = boardRect.left +\r
2377       lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);\r
2378     y1 = boardRect.top +\r
2379       lineGap/2 + y * (squareSize + lineGap);\r
2380   } else {\r
2381     x1 = boardRect.left +\r
2382       lineGap/2 + x * (squareSize + lineGap);\r
2383     y1 = boardRect.top +\r
2384       lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);\r
2385   }\r
2386   hPen = pen ? premovePen : highlightPen;\r
2387   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2388   MoveToEx(hdc, x1, y1, NULL);\r
2389   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2390   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2391   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2392   LineTo(hdc, x1, y1);\r
2393   SelectObject(hdc, oldPen);\r
2394 }\r
2395 \r
2396 VOID\r
2397 DrawHighlightsOnDC(HDC hdc)\r
2398 {\r
2399   int i;\r
2400   for (i=0; i<2; i++) {\r
2401     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
2402       DrawHighlightOnDC(hdc, TRUE,\r
2403                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
2404                         HIGHLIGHT_PEN);\r
2405   }\r
2406   for (i=0; i<2; i++) {\r
2407     if (premoveHighlightInfo.sq[i].x >= 0 && \r
2408         premoveHighlightInfo.sq[i].y >= 0) {\r
2409         DrawHighlightOnDC(hdc, TRUE,\r
2410                           premoveHighlightInfo.sq[i].x, \r
2411                           premoveHighlightInfo.sq[i].y,\r
2412                           PREMOVE_PEN);\r
2413     }\r
2414   }\r
2415 }\r
2416 \r
2417 /* Note: sqcolor is used only in monoMode */\r
2418 /* Note that this code is largely duplicated in woptions.c,\r
2419    function DrawSampleSquare, so that needs to be updated too */\r
2420 VOID\r
2421 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2422 {\r
2423   HBITMAP oldBitmap;\r
2424   HBRUSH oldBrush;\r
2425 \r
2426   if (appData.blindfold) return;\r
2427 \r
2428   if (appData.monoMode) {\r
2429     SelectObject(tmphdc, PieceBitmap(piece, \r
2430       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2431     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2432            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2433   } else {\r
2434     if (color) {\r
2435       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2436       oldBrush = SelectObject(hdc, whitePieceBrush);\r
2437       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2438 #if 0\r
2439       /* Use black piece color for outline of white pieces */\r
2440       /* Not sure this looks really good (though xboard does it).\r
2441          Maybe better to have another selectable color, default black */\r
2442       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
2443       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2444       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2445 #else\r
2446       /* Use black for outline of white pieces */\r
2447       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2448       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);\r
2449 #endif\r
2450     } else {\r
2451 #if 0\r
2452       /* Use white piece color for details of black pieces */\r
2453       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
2454          WHITE_PIECE ones aren't always the right shape. */\r
2455       /* Not sure this looks really good (though xboard does it).\r
2456          Maybe better to have another selectable color, default medium gray? */\r
2457       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
2458       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
2459       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2460       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2461       SelectObject(hdc, blackPieceBrush);\r
2462       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2463 #else\r
2464       /* Use square color for details of black pieces */\r
2465       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2466       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2467       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2468 #endif\r
2469     }\r
2470     SelectObject(hdc, oldBrush);\r
2471     SelectObject(tmphdc, oldBitmap);\r
2472   }\r
2473 }\r
2474 \r
2475 VOID\r
2476 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2477 {\r
2478   int row, column, x, y, square_color, piece_color;\r
2479   ChessSquare piece;\r
2480   HBRUSH oldBrush;\r
2481 \r
2482   for (row = 0; row < BOARD_SIZE; row++) {\r
2483     for (column = 0; column < BOARD_SIZE; column++) {\r
2484   \r
2485       SquareToPos(row, column, &x, &y);\r
2486 \r
2487       piece = board[row][column];\r
2488 \r
2489       square_color = ((column + row) % 2) == 1;\r
2490       piece_color = (int) piece < (int) BlackPawn;\r
2491 \r
2492       if (appData.monoMode) {\r
2493         if (piece == EmptySquare) {\r
2494           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
2495                  square_color ? WHITENESS : BLACKNESS);\r
2496         } else {\r
2497           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
2498         }\r
2499       } else {\r
2500         oldBrush = SelectObject(hdc, square_color ?\r
2501                                 lightSquareBrush : darkSquareBrush);\r
2502         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
2503         SelectObject(hdc, oldBrush);\r
2504         if (piece != EmptySquare)\r
2505           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
2506       }\r
2507     }\r
2508   }\r
2509 }\r
2510 \r
2511 #define MAX_CLIPS 200   /* more than enough */\r
2512 \r
2513 VOID\r
2514 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
2515 {\r
2516   static Board lastReq, lastDrawn;\r
2517   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
2518   static int lastDrawnFlipView = 0;\r
2519   static int lastReqValid = 0, lastDrawnValid = 0;\r
2520   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
2521   HDC tmphdc;\r
2522   HDC hdcmem;\r
2523   HBITMAP bufferBitmap;\r
2524   HBITMAP oldBitmap;\r
2525   RECT Rect;\r
2526   HRGN clips[MAX_CLIPS];\r
2527   ChessSquare dragged_piece = EmptySquare;\r
2528 \r
2529   /* I'm undecided on this - this function figures out whether a full\r
2530    * repaint is necessary on its own, so there's no real reason to have the\r
2531    * caller tell it that.  I think this can safely be set to FALSE - but\r
2532    * if we trust the callers not to request full repaints unnessesarily, then\r
2533    * we could skip some clipping work.  In other words, only request a full\r
2534    * redraw when the majority of pieces have changed positions (ie. flip, \r
2535    * gamestart and similar)  --Hawk\r
2536    */\r
2537   Boolean fullrepaint = repaint;\r
2538 \r
2539   if (board == NULL) {\r
2540     if (!lastReqValid) {\r
2541       return;\r
2542     }\r
2543     board = lastReq;\r
2544   } else {\r
2545     CopyBoard(lastReq, board);\r
2546     lastReqValid = 1;\r
2547   }\r
2548 \r
2549   if (doingSizing) {\r
2550     return;\r
2551   }\r
2552 \r
2553   if (IsIconic(hwndMain)) {\r
2554     return;\r
2555   }\r
2556 \r
2557   if (hdc == NULL) {\r
2558     hdc = GetDC(hwndMain);\r
2559     if (!appData.monoMode) {\r
2560       SelectPalette(hdc, hPal, FALSE);\r
2561       RealizePalette(hdc);\r
2562     }\r
2563     releaseDC = TRUE;\r
2564   } else {\r
2565     releaseDC = FALSE;\r
2566   }\r
2567 \r
2568 #if 0\r
2569   fprintf(debugFP, "*******************************\n"\r
2570                    "repaint = %s\n"\r
2571                    "dragInfo.from (%d,%d)\n"\r
2572                    "dragInfo.start (%d,%d)\n"\r
2573                    "dragInfo.pos (%d,%d)\n"\r
2574                    "dragInfo.lastpos (%d,%d)\n", \r
2575                     repaint ? "TRUE" : "FALSE",\r
2576                     dragInfo.from.x, dragInfo.from.y, \r
2577                     dragInfo.start.x, dragInfo.start.y,\r
2578                     dragInfo.pos.x, dragInfo.pos.y,\r
2579                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
2580   fprintf(debugFP, "prev:  ");\r
2581   for (row = 0; row < 8; row++) {\r
2582     for (column = 0; column < 8; column++) {\r
2583       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
2584     }\r
2585   }\r
2586   fprintf(debugFP, "\n");\r
2587   fprintf(debugFP, "board: ");\r
2588   for (row = 0; row < 8; row++) {\r
2589     for (column = 0; column < 8; column++) {\r
2590       fprintf(debugFP, "%d ", board[row][column]);\r
2591     }\r
2592   }\r
2593   fprintf(debugFP, "\n");\r
2594   fflush(debugFP);\r
2595 #endif\r
2596 \r
2597   /* Create some work-DCs */\r
2598   hdcmem = CreateCompatibleDC(hdc);\r
2599   tmphdc = CreateCompatibleDC(hdc);\r
2600 \r
2601   /* Figure out which squares need updating by comparing the \r
2602    * newest board with the last drawn board and checking if\r
2603    * flipping has changed.\r
2604    */\r
2605   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
2606     for (row = 0; row < 8; row++) {\r
2607       for (column = 0; column < 8; column++) {\r
2608         if (lastDrawn[row][column] != board[row][column]) {\r
2609           SquareToPos(row, column, &x, &y);\r
2610           clips[num_clips++] =\r
2611             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
2612         }\r
2613       }\r
2614     }\r
2615     for (i=0; i<2; i++) {\r
2616       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
2617           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
2618         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
2619             lastDrawnHighlight.sq[i].y >= 0) {\r
2620           SquareToPos(lastDrawnHighlight.sq[i].y,\r
2621                       lastDrawnHighlight.sq[i].x, &x, &y);\r
2622           clips[num_clips++] =\r
2623             CreateRectRgn(x - lineGap, y - lineGap, \r
2624                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2625         }\r
2626         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
2627           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
2628           clips[num_clips++] =\r
2629             CreateRectRgn(x - lineGap, y - lineGap, \r
2630                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2631         }\r
2632       }\r
2633     }\r
2634     for (i=0; i<2; i++) {\r
2635       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
2636           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
2637         if (lastDrawnPremove.sq[i].x >= 0 &&\r
2638             lastDrawnPremove.sq[i].y >= 0) {\r
2639           SquareToPos(lastDrawnPremove.sq[i].y,\r
2640                       lastDrawnPremove.sq[i].x, &x, &y);\r
2641           clips[num_clips++] =\r
2642             CreateRectRgn(x - lineGap, y - lineGap, \r
2643                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2644         }\r
2645         if (premoveHighlightInfo.sq[i].x >= 0 && \r
2646             premoveHighlightInfo.sq[i].y >= 0) {\r
2647           SquareToPos(premoveHighlightInfo.sq[i].y, \r
2648                       premoveHighlightInfo.sq[i].x, &x, &y);\r
2649           clips[num_clips++] =\r
2650             CreateRectRgn(x - lineGap, y - lineGap, \r
2651                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2652         }\r
2653       }\r
2654     }\r
2655   } else {\r
2656     fullrepaint = TRUE;\r
2657   }\r
2658 \r
2659   /* Create a buffer bitmap - this is the actual bitmap\r
2660    * being written to.  When all the work is done, we can\r
2661    * copy it to the real DC (the screen).  This avoids\r
2662    * the problems with flickering.\r
2663    */\r
2664   GetClientRect(hwndMain, &Rect);\r
2665   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
2666                                         Rect.bottom-Rect.top+1);\r
2667   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
2668   if (!appData.monoMode) {\r
2669     SelectPalette(hdcmem, hPal, FALSE);\r
2670   }\r
2671 \r
2672   /* Create clips for dragging */\r
2673   if (!fullrepaint) {\r
2674     if (dragInfo.from.x >= 0) {\r
2675       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
2676       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2677     }\r
2678     if (dragInfo.start.x >= 0) {\r
2679       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
2680       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2681     }\r
2682     if (dragInfo.pos.x >= 0) {\r
2683       x = dragInfo.pos.x - squareSize / 2;\r
2684       y = dragInfo.pos.y - squareSize / 2;\r
2685       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2686     }\r
2687     if (dragInfo.lastpos.x >= 0) {\r
2688       x = dragInfo.lastpos.x - squareSize / 2;\r
2689       y = dragInfo.lastpos.y - squareSize / 2;\r
2690       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2691     }\r
2692   }\r
2693 \r
2694   /* If dragging is in progress, we temporarely remove the piece */\r
2695   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
2696     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
2697     board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
2698   }\r
2699 \r
2700   /* Are we animating a move?  \r
2701    * If so, \r
2702    *   - remove the piece from the board (temporarely)\r
2703    *   - calculate the clipping region\r
2704    */\r
2705   if (!fullrepaint) {\r
2706     if (animInfo.piece != EmptySquare) {\r
2707       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
2708       x = boardRect.left + animInfo.lastpos.x;\r
2709       y = boardRect.top + animInfo.lastpos.y;\r
2710       x2 = boardRect.left + animInfo.pos.x;\r
2711       y2 = boardRect.top + animInfo.pos.y;\r
2712       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
2713       /* Slight kludge.  The real problem is that after AnimateMove is\r
2714          done, the position on the screen does not match lastDrawn.\r
2715          This currently causes trouble only on e.p. captures in\r
2716          atomic, where the piece moves to an empty square and then\r
2717          explodes.  The old and new positions both had an empty square\r
2718          at the destination, but animation has drawn a piece there and\r
2719          we have to remember to erase it. */\r
2720       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
2721     }\r
2722   }\r
2723 \r
2724   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
2725   if (num_clips == 0)\r
2726     fullrepaint = TRUE;\r
2727 \r
2728   /* Set clipping on the memory DC */\r
2729   if (!fullrepaint) {\r
2730     SelectClipRgn(hdcmem, clips[0]);\r
2731     for (x = 1; x < num_clips; x++) {\r
2732       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
2733         abort();  // this should never ever happen!\r
2734     }\r
2735   }\r
2736 \r
2737   /* Do all the drawing to the memory DC */\r
2738   DrawGridOnDC(hdcmem);\r
2739   DrawHighlightsOnDC(hdcmem);\r
2740   DrawBoardOnDC(hdcmem, board, tmphdc);\r
2741   DrawCoordsOnDC(hdcmem);\r
2742 \r
2743   /* Put the dragged piece back into place and draw it */\r
2744   if (dragged_piece != EmptySquare) {\r
2745     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
2746     x = dragInfo.pos.x - squareSize / 2;\r
2747     y = dragInfo.pos.y - squareSize / 2;\r
2748     DrawPieceOnDC(hdcmem, dragged_piece,\r
2749                   ((int) dragged_piece < (int) BlackPawn), \r
2750                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
2751   }   \r
2752   \r
2753   /* Put the animated piece back into place and draw it */\r
2754   if (animInfo.piece != EmptySquare) {\r
2755     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
2756     x = boardRect.left + animInfo.pos.x;\r
2757     y = boardRect.top + animInfo.pos.y;\r
2758     DrawPieceOnDC(hdcmem, animInfo.piece,\r
2759                   ((int) animInfo.piece < (int) BlackPawn),\r
2760                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
2761   }\r
2762 \r
2763   /* Release the bufferBitmap by selecting in the old bitmap \r
2764    * and delete the memory DC\r
2765    */\r
2766   SelectObject(hdcmem, oldBitmap);\r
2767   DeleteDC(hdcmem);\r
2768 \r
2769   /* Set clipping on the target DC */\r
2770   if (!fullrepaint) {\r
2771     SelectClipRgn(hdc, clips[0]);\r
2772     for (x = 1; x < num_clips; x++) {\r
2773       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
2774         abort();   // this should never ever happen!\r
2775     } \r
2776   }\r
2777 \r
2778   /* Copy the new bitmap onto the screen in one go.\r
2779    * This way we avoid any flickering\r
2780    */\r
2781   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
2782   BitBlt(hdc, boardRect.left, boardRect.top,\r
2783          boardRect.right - boardRect.left,\r
2784          boardRect.bottom - boardRect.top,\r
2785          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
2786   SelectObject(tmphdc, oldBitmap);\r
2787 \r
2788   /* Massive cleanup */\r
2789   for (x = 0; x < num_clips; x++)\r
2790     DeleteObject(clips[x]);\r
2791 \r
2792   DeleteDC(tmphdc);\r
2793   DeleteObject(bufferBitmap);\r
2794 \r
2795   if (releaseDC) \r
2796     ReleaseDC(hwndMain, hdc);\r
2797   \r
2798   if (lastDrawnFlipView != flipView) {\r
2799     if (flipView)\r
2800       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
2801     else\r
2802       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
2803   }\r
2804 \r
2805   CopyBoard(lastDrawn, board);\r
2806   lastDrawnHighlight = highlightInfo;\r
2807   lastDrawnPremove   = premoveHighlightInfo;\r
2808   lastDrawnFlipView = flipView;\r
2809   lastDrawnValid = 1;\r
2810 }\r
2811 \r
2812 \r
2813 /*---------------------------------------------------------------------------*\\r
2814 | CLIENT PAINT PROCEDURE\r
2815 |   This is the main event-handler for the WM_PAINT message.\r
2816 |\r
2817 \*---------------------------------------------------------------------------*/\r
2818 VOID\r
2819 PaintProc(HWND hwnd)\r
2820 {\r
2821   HDC         hdc;\r
2822   PAINTSTRUCT ps;\r
2823   HFONT       oldFont;\r
2824 \r
2825   if(hdc = BeginPaint(hwnd, &ps)) {\r
2826     if (IsIconic(hwnd)) {\r
2827       DrawIcon(hdc, 2, 2, iconCurrent);\r
2828     } else {\r
2829       if (!appData.monoMode) {\r
2830         SelectPalette(hdc, hPal, FALSE);\r
2831         RealizePalette(hdc);\r
2832       }\r
2833       HDCDrawPosition(hdc, 1, NULL);\r
2834       oldFont =\r
2835         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2836       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
2837                  ETO_CLIPPED|ETO_OPAQUE,\r
2838                  &messageRect, messageText, strlen(messageText), NULL);\r
2839       SelectObject(hdc, oldFont);\r
2840       DisplayBothClocks();\r
2841     }\r
2842     EndPaint(hwnd,&ps);\r
2843   }\r
2844 \r
2845   return;\r
2846 }\r
2847 \r
2848 \r
2849 /*\r
2850  * If the user selects on a border boundary, return -1; if off the board,\r
2851  *   return -2.  Otherwise map the event coordinate to the square.\r
2852  * The offset boardRect.left or boardRect.top must already have been\r
2853  *   subtracted from x.\r
2854  */\r
2855 int\r
2856 EventToSquare(int x)\r
2857 {\r
2858   if (x <= 0)\r
2859     return -2;\r
2860   if (x < lineGap)\r
2861     return -1;\r
2862   x -= lineGap;\r
2863   if ((x % (squareSize + lineGap)) >= squareSize)\r
2864     return -1;\r
2865   x /= (squareSize + lineGap);\r
2866   if (x >= BOARD_SIZE)\r
2867     return -2;\r
2868   return x;\r
2869 }\r
2870 \r
2871 typedef struct {\r
2872   char piece;\r
2873   int command;\r
2874   char* name;\r
2875 } DropEnable;\r
2876 \r
2877 DropEnable dropEnables[] = {\r
2878   { 'P', DP_Pawn, "Pawn" },\r
2879   { 'N', DP_Knight, "Knight" },\r
2880   { 'B', DP_Bishop, "Bishop" },\r
2881   { 'R', DP_Rook, "Rook" },\r
2882   { 'Q', DP_Queen, "Queen" },\r
2883 };\r
2884 \r
2885 VOID\r
2886 SetupDropMenu(HMENU hmenu)\r
2887 {\r
2888   int i, count, enable;\r
2889   char *p;\r
2890   extern char white_holding[], black_holding[];\r
2891   char item[MSG_SIZ];\r
2892 \r
2893   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
2894     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
2895                dropEnables[i].piece);\r
2896     count = 0;\r
2897     while (p && *p++ == dropEnables[i].piece) count++;\r
2898     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
2899     enable = count > 0 || !appData.testLegality\r
2900       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
2901                       && !appData.icsActive);\r
2902     ModifyMenu(hmenu, dropEnables[i].command,\r
2903                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
2904                dropEnables[i].command, item);\r
2905   }\r
2906 }\r
2907 \r
2908 static int fromX = -1, fromY = -1, toX, toY;\r
2909 \r
2910 /* Event handler for mouse messages */\r
2911 VOID\r
2912 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
2913 {\r
2914   int x, y;\r
2915   POINT pt;\r
2916   static int recursive = 0;\r
2917   HMENU hmenu;\r
2918   BOOLEAN saveAnimate;\r
2919   static BOOLEAN sameAgain = FALSE;\r
2920 \r
2921   if (recursive) {\r
2922     if (message == WM_MBUTTONUP) {\r
2923       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
2924          to the middle button: we simulate pressing the left button too!\r
2925          */\r
2926       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
2927       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
2928     }\r
2929     return;\r
2930   }\r
2931   recursive++;\r
2932   \r
2933   pt.x = LOWORD(lParam);\r
2934   pt.y = HIWORD(lParam);\r
2935   x = EventToSquare(pt.x - boardRect.left);\r
2936   y = EventToSquare(pt.y - boardRect.top);\r
2937   if (!flipView && y >= 0) {\r
2938     y = BOARD_SIZE - 1 - y;\r
2939   }\r
2940   if (flipView && x >= 0) {\r
2941     x = BOARD_SIZE - 1 - x;\r
2942   }\r
2943 \r
2944   switch (message) {\r
2945   case WM_LBUTTONDOWN:\r
2946     ErrorPopDown();\r
2947     sameAgain = FALSE;\r
2948     if (y == -2) {\r
2949       /* Downclick vertically off board; check if on clock */\r
2950       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
2951         if (gameMode == EditPosition) {\r
2952           SetWhiteToPlayEvent();\r
2953         } else if (gameMode == IcsPlayingBlack ||\r
2954                    gameMode == MachinePlaysWhite) {\r
2955           CallFlagEvent();\r
2956         }\r
2957       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
2958         if (gameMode == EditPosition) {\r
2959           SetBlackToPlayEvent();\r
2960         } else if (gameMode == IcsPlayingWhite ||\r
2961                    gameMode == MachinePlaysBlack) {\r
2962           CallFlagEvent();\r
2963         }\r
2964       }\r
2965       if (!appData.highlightLastMove) {\r
2966         ClearHighlights();\r
2967         DrawPosition(FALSE, NULL);\r
2968       }\r
2969       fromX = fromY = -1;\r
2970       dragInfo.start.x = dragInfo.start.y = -1;\r
2971       dragInfo.from = dragInfo.start;\r
2972       break;\r
2973     } else if (x < 0 || y < 0) {\r
2974       break;\r
2975     } else if (fromX == x && fromY == y) {\r
2976       /* Downclick on same square again */\r
2977       ClearHighlights();\r
2978       DrawPosition(FALSE, NULL);\r
2979       sameAgain = TRUE;  \r
2980     } else if (fromX != -1) {\r
2981       /* Downclick on different square */\r
2982       ChessSquare pdown, pup;\r
2983       pdown = boards[currentMove][fromY][fromX];\r
2984       pup = boards[currentMove][y][x];\r
2985       if (gameMode == EditPosition ||\r
2986           !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
2987              WhitePawn <= pup && pup <= WhiteKing) ||\r
2988             (BlackPawn <= pdown && pdown <= BlackKing &&\r
2989              BlackPawn <= pup && pup <= BlackKing))) {\r
2990         /* EditPosition, empty square, or different color piece;\r
2991            click-click move is possible */\r
2992         toX = x;\r
2993         toY = y;\r
2994         if (IsPromotion(fromX, fromY, toX, toY)) {\r
2995           if (appData.alwaysPromoteToQueen) {\r
2996             UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
2997             if (!appData.highlightLastMove) {\r
2998               ClearHighlights();\r
2999               DrawPosition(FALSE, NULL);\r
3000             }\r
3001           } else {\r
3002             SetHighlights(fromX, fromY, toX, toY);\r
3003             DrawPosition(FALSE, NULL);\r
3004             PromotionPopup(hwnd);\r
3005           }\r
3006         } else {        /* not a promotion */\r
3007           if (appData.animate || appData.highlightLastMove) {\r
3008             SetHighlights(fromX, fromY, toX, toY);\r
3009           } else {\r
3010             ClearHighlights();\r
3011           }\r
3012           UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
3013           if (appData.animate && !appData.highlightLastMove) {\r
3014             ClearHighlights();\r
3015             DrawPosition(FALSE, NULL);\r
3016           }\r
3017         }\r
3018         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
3019         fromX = fromY = -1;\r
3020         break;\r
3021       }\r
3022       ClearHighlights();\r
3023       DrawPosition(FALSE, NULL);\r
3024     }\r
3025     /* First downclick, or restart on a square with same color piece */\r
3026     if (!frozen && OKToStartUserMove(x, y)) {\r
3027       fromX = x;\r
3028       fromY = y;\r
3029       dragInfo.lastpos = pt;\r
3030       dragInfo.from.x = fromX;\r
3031       dragInfo.from.y = fromY;\r
3032       dragInfo.start = dragInfo.from;\r
3033       SetCapture(hwndMain);\r
3034     } else {\r
3035       fromX = fromY = -1;\r
3036       dragInfo.start.x = dragInfo.start.y = -1;\r
3037       dragInfo.from = dragInfo.start;\r
3038     }\r
3039     break;\r
3040 \r
3041   case WM_LBUTTONUP:\r
3042     ReleaseCapture();\r
3043     if (fromX == -1) break;\r
3044     if (x == fromX && y == fromY) {\r
3045       dragInfo.from.x = dragInfo.from.y = -1;\r
3046       /* Upclick on same square */\r
3047       if (sameAgain) {\r
3048         /* Clicked same square twice: abort click-click move */\r
3049         fromX = fromY = -1;\r
3050         gotPremove = 0;\r
3051         ClearPremoveHighlights();\r
3052       } else {\r
3053         /* First square clicked: start click-click move */\r
3054         SetHighlights(fromX, fromY, -1, -1);\r
3055       }\r
3056       DrawPosition(FALSE, NULL);\r
3057     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
3058       /* Errant click; ignore */\r
3059       break;\r
3060     } else {\r
3061       /* Finish drag move */\r
3062       dragInfo.from.x = dragInfo.from.y = -1;\r
3063       toX = x;\r
3064       toY = y;\r
3065       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
3066       appData.animate = appData.animate && !appData.animateDragging;\r
3067       if (IsPromotion(fromX, fromY, toX, toY)) {\r
3068         if (appData.alwaysPromoteToQueen) {\r
3069           UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
3070         } else {\r
3071           DrawPosition(FALSE, NULL);\r
3072           PromotionPopup(hwnd);\r
3073         }\r
3074       } else {\r
3075         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
3076       }\r
3077       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
3078       appData.animate = saveAnimate;\r
3079       fromX = fromY = -1;\r
3080       if (appData.highlightDragging && !appData.highlightLastMove) {\r
3081         ClearHighlights();\r
3082       }\r
3083       if (appData.animate || appData.animateDragging ||\r
3084           appData.highlightDragging || gotPremove) {\r
3085         DrawPosition(FALSE, NULL);\r
3086       }\r
3087     }\r
3088     dragInfo.start.x = dragInfo.start.y = -1; \r
3089     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
3090     break;\r
3091 \r
3092   case WM_MOUSEMOVE:\r
3093     if ((appData.animateDragging || appData.highlightDragging)\r
3094         && (wParam & MK_LBUTTON)\r
3095         && dragInfo.from.x >= 0) {\r
3096       if (appData.animateDragging) {\r
3097         dragInfo.pos = pt;\r
3098       }\r
3099       if (appData.highlightDragging) {\r
3100         SetHighlights(fromX, fromY, x, y);\r
3101       }\r
3102       DrawPosition(FALSE, NULL);\r
3103       dragInfo.lastpos = dragInfo.pos;\r
3104     }\r
3105     break;\r
3106   case WM_MOUSEWHEEL:\r
3107         /* Mouse Wheel is being rolled forward \r
3108          * Play moves forward\r
3109          */\r
3110         if ((short)HIWORD(wParam) > 0) \r
3111            if (forwardMostMove > 0 && currentMove != forwardMostMove)\r
3112                    ForwardEvent();\r
3113            /* Mouse Wheel is being rolled backward \r
3114             * Play moves backward\r
3115             */\r
3116         if ((short)HIWORD(wParam) < 0) \r
3117            if (currentMove > 0) BackwardEvent();\r
3118         break;\r
3119   case WM_MBUTTONDOWN:\r
3120   case WM_RBUTTONDOWN:\r
3121     ErrorPopDown();\r
3122     ReleaseCapture();\r
3123     fromX = fromY = -1;\r
3124     dragInfo.pos.x = dragInfo.pos.y = -1;\r
3125     dragInfo.start.x = dragInfo.start.y = -1;\r
3126     dragInfo.from = dragInfo.start;\r
3127     dragInfo.lastpos = dragInfo.pos;\r
3128     if (appData.highlightDragging) {\r
3129       ClearHighlights();\r
3130     }\r
3131     DrawPosition(TRUE, NULL);\r
3132 \r
3133     switch (gameMode) {\r
3134     case EditPosition:\r
3135     case IcsExamining:\r
3136       if (x < 0 || y < 0) break;\r
3137       fromX = x;\r
3138       fromY = y;\r
3139       if (message == WM_MBUTTONDOWN) {\r
3140         buttonCount = 3;  /* even if system didn't think so */\r
3141         if (wParam & MK_SHIFT) \r
3142           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3143         else\r
3144           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3145       } else { /* message == WM_RBUTTONDOWN */\r
3146 #if 0\r
3147         if (buttonCount == 3) {\r
3148           if (wParam & MK_SHIFT) \r
3149             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3150           else\r
3151             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3152         } else {\r
3153           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3154         }\r
3155 #else\r
3156         /* Just have one menu, on the right button.  Windows users don't\r
3157            think to try the middle one, and sometimes other software steals\r
3158            it, or it doesn't really exist. */\r
3159         MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3160 #endif\r
3161       }\r
3162       break;\r
3163     case IcsPlayingWhite:\r
3164     case IcsPlayingBlack:\r
3165     case EditGame:\r
3166     case MachinePlaysWhite:\r
3167     case MachinePlaysBlack:\r
3168       if (appData.testLegality &&\r
3169           gameInfo.variant != VariantBughouse &&\r
3170           gameInfo.variant != VariantCrazyhouse) break;\r
3171       if (x < 0 || y < 0) break;\r
3172       fromX = x;\r
3173       fromY = y;\r
3174       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3175       SetupDropMenu(hmenu);\r
3176       MenuPopup(hwnd, pt, hmenu, -1);\r
3177       break;\r
3178     default:\r
3179       break;\r
3180     }\r
3181     break;\r
3182   }\r
3183 \r
3184   recursive--;\r
3185 }\r
3186 \r
3187 /* Preprocess messages for buttons in main window */\r
3188 LRESULT CALLBACK\r
3189 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3190 {\r
3191   int id = GetWindowLong(hwnd, GWL_ID);\r
3192   int i, dir;\r
3193 \r
3194   for (i=0; i<N_BUTTONS; i++) {\r
3195     if (buttonDesc[i].id == id) break;\r
3196   }\r
3197   if (i == N_BUTTONS) return 0;\r
3198   switch (message) {\r
3199   case WM_KEYDOWN:\r
3200     switch (wParam) {\r
3201     case VK_LEFT:\r
3202     case VK_RIGHT:\r
3203       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3204       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3205       return TRUE;\r
3206     }\r
3207     break;\r
3208   case WM_CHAR:\r
3209     switch (wParam) {\r
3210     case '\r':\r
3211       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3212       return TRUE;\r
3213     case '\t':\r
3214       if (appData.icsActive) {\r
3215         if (GetKeyState(VK_SHIFT) < 0) {\r
3216           /* shifted */\r
3217           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3218           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3219           SetFocus(h);\r
3220         } else {\r
3221           /* unshifted */\r
3222           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3223           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3224           SetFocus(h);\r
3225         }\r
3226         return TRUE;\r
3227       }\r
3228       break;\r
3229     default:\r
3230       if (appData.icsActive) {\r
3231         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3232         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3233         SetFocus(h);\r
3234         SendMessage(h, WM_CHAR, wParam, lParam);\r
3235         return TRUE;\r
3236       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3237         PopUpMoveDialog((char)wParam);\r
3238       }\r
3239       break;\r
3240     }\r