Initial checkin. I created this by combining the XBoard 4.2.6 and
[xboard.git] / winboard / winboard.c
1 /* \r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id$\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
7  *\r
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
9  * which was written and is copyrighted by Wayne Christopher.\r
10  *\r
11  * The following terms apply to Digital Equipment Corporation's copyright\r
12  * interest in XBoard:\r
13  * ------------------------------------------------------------------------\r
14  * All Rights Reserved\r
15  *\r
16  * Permission to use, copy, modify, and distribute this software and its\r
17  * documentation for any purpose and without fee is hereby granted,\r
18  * provided that the above copyright notice appear in all copies and that\r
19  * both that copyright notice and this permission notice appear in\r
20  * supporting documentation, and that the name of Digital not be\r
21  * used in advertising or publicity pertaining to distribution of the\r
22  * software without specific, written prior permission.\r
23  *\r
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
30  * SOFTWARE.\r
31  * ------------------------------------------------------------------------\r
32  *\r
33  * The following terms apply to the enhanced version of XBoard distributed\r
34  * by the Free Software Foundation:\r
35  * ------------------------------------------------------------------------\r
36  * This program is free software; you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation; either version 2 of the License, or\r
39  * (at your option) any later version.\r
40  *\r
41  * This program is distributed in the hope that it will be useful,\r
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
44  * GNU General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program; if not, write to the Free Software\r
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
49  * ------------------------------------------------------------------------\r
50  */\r
51 \r
52 #include "config.h"\r
53 \r
54 #include <windows.h>\r
55 #include <winuser.h>\r
56 \r
57 #include <stdio.h>\r
58 #include <stdlib.h>\r
59 #include <malloc.h>\r
60 #include <io.h>\r
61 #include <sys/stat.h>\r
62 #include <fcntl.h>\r
63 #include <math.h>\r
64 #include <commdlg.h>\r
65 #include <dlgs.h>\r
66 #include <richedit.h>\r
67 \r
68 #if __GNUC__\r
69 #include <errno.h>\r
70 #include <string.h>\r
71 #endif\r
72 \r
73 #include "common.h"\r
74 #include "winboard.h"\r
75 #include "frontend.h"\r
76 #include "backend.h"\r
77 #include "moves.h"\r
78 #include "wclipbrd.h"\r
79 #include "wgamelist.h"\r
80 #include "wedittags.h"\r
81 #include "woptions.h"\r
82 #include "wsockerr.h"\r
83 #include "defaults.h"\r
84 \r
85 typedef struct {\r
86   ChessSquare piece;  \r
87   POINT pos;      /* window coordinates of current pos */\r
88   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
89   POINT from;     /* board coordinates of the piece's orig pos */\r
90   POINT to;       /* board coordinates of the piece's new pos */\r
91 } AnimInfo;\r
92 \r
93 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
94 \r
95 typedef struct {\r
96   POINT start;    /* window coordinates of start pos */\r
97   POINT pos;      /* window coordinates of current pos */\r
98   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
99   POINT from;     /* board coordinates of the piece's orig pos */\r
100 } DragInfo;\r
101 \r
102 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
103 \r
104 typedef struct {\r
105   POINT sq[2];    /* board coordinates of from, to squares */\r
106 } HighlightInfo;\r
107 \r
108 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
109 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
110 \r
111 /* Window class names */\r
112 char szAppName[] = "WinBoard";\r
113 char szConsoleName[] = "WBConsole";\r
114 \r
115 /* Title bar text */\r
116 char szTitle[] = "WinBoard";\r
117 char szConsoleTitle[] = "ICS Interaction";\r
118 \r
119 char *programName;\r
120 char *settingsFileName;\r
121 BOOLEAN saveSettingsOnExit;\r
122 char installDir[MSG_SIZ];\r
123 \r
124 BoardSize boardSize;\r
125 BOOLEAN chessProgram;\r
126 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;\r
127 static int squareSize, lineGap;\r
128 static int winWidth, winHeight;\r
129 static RECT messageRect, whiteRect, blackRect;\r
130 static char messageText[MESSAGE_TEXT_MAX];\r
131 static int clockTimerEvent = 0;\r
132 static int loadGameTimerEvent = 0;\r
133 static int analysisTimerEvent = 0;\r
134 static DelayedEventCallback delayedTimerCallback;\r
135 static int delayedTimerEvent = 0;\r
136 static int buttonCount = 2;\r
137 char *icsTextMenuString;\r
138 char *icsNames;\r
139 char *firstChessProgramNames;\r
140 char *secondChessProgramNames;\r
141 \r
142 #define ARG_MAX 20000\r
143 \r
144 #define PALETTESIZE 256\r
145 \r
146 HINSTANCE hInst;          /* current instance */\r
147 HWND hwndMain = NULL;        /* root window*/\r
148 HWND hwndConsole = NULL;\r
149 BOOLEAN alwaysOnTop = FALSE;\r
150 RECT boardRect;\r
151 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
152   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
153 HPALETTE hPal;\r
154 ColorClass currentColorClass;\r
155 \r
156 HWND hCommPort = NULL;    /* currently open comm port */\r
157 static HWND hwndPause;    /* pause button */\r
158 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];\r
159 static HBRUSH lightSquareBrush, darkSquareBrush,\r
160   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;\r
161 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
162 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
163 static HPEN gridPen = NULL;\r
164 static HPEN highlightPen = NULL;\r
165 static HPEN premovePen = NULL;\r
166 static NPLOGPALETTE pLogPal;\r
167 static BOOL paletteChanged = FALSE;\r
168 static HICON iconWhite, iconBlack, iconCurrent;\r
169 static int doingSizing = FALSE;\r
170 static int lastSizing = 0;\r
171 \r
172 #if __GNUC__ && !defined(_winmajor)\r
173 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
174 #else\r
175 #define oldDialog (_winmajor < 4)\r
176 #endif\r
177 \r
178 char *defaultTextAttribs[] = \r
179 {\r
180   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
181   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
182   COLOR_NONE\r
183 };\r
184 \r
185 typedef struct {\r
186   char *name;\r
187   int squareSize;\r
188   int lineGap;\r
189   int smallLayout;\r
190   int tinyLayout;\r
191   int cliWidth, cliHeight;\r
192 } SizeInfo;\r
193 \r
194 SizeInfo sizeInfo[] = \r
195 {\r
196   { "tiny",     21, 0, 1, 1, 0, 0 },\r
197   { "teeny",    25, 1, 1, 1, 0, 0 },\r
198   { "dinky",    29, 1, 1, 1, 0, 0 },\r
199   { "petite",   33, 1, 1, 1, 0, 0 },\r
200   { "slim",     37, 2, 1, 0, 0, 0 },\r
201   { "small",    40, 2, 1, 0, 0, 0 },\r
202   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
203   { "middling", 49, 2, 0, 0, 0, 0 },\r
204   { "average",  54, 2, 0, 0, 0, 0 },\r
205   { "moderate", 58, 3, 0, 0, 0, 0 },\r
206   { "medium",   64, 3, 0, 0, 0, 0 },\r
207   { "bulky",    72, 3, 0, 0, 0, 0 },\r
208   { "large",    80, 3, 0, 0, 0, 0 },\r
209   { "big",      87, 3, 0, 0, 0, 0 },\r
210   { "huge",     95, 3, 0, 0, 0, 0 },\r
211   { "giant",    108, 3, 0, 0, 0, 0 },\r
212   { "colossal", 116, 4, 0, 0, 0, 0 },\r
213   { "titanic",  129, 4, 0, 0, 0, 0 },\r
214   { NULL, 0, 0, 0, 0, 0, 0 }\r
215 };\r
216 \r
217 #define MF(x) {x, {0, }, {0, }, 0}\r
218 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
219 {\r
220   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), \r
221     MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),\r
222     MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },\r
223   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), \r
224     MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),\r
225     MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },\r
226   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),\r
227     MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),\r
228     MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },\r
229   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),\r
230     MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),\r
231     MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },\r
232   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),\r
233     MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),\r
234     MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },\r
235   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),\r
236     MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),\r
237     MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },\r
238   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),\r
239     MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),\r
240     MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },\r
241   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),\r
242     MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),\r
243     MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },\r
244   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),\r
245     MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),\r
246     MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },\r
247   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),\r
248     MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),\r
249     MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },\r
250   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),\r
251     MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),\r
252     MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },\r
253   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),\r
254     MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),\r
255     MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },\r
256   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),\r
257     MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),\r
258     MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },\r
259   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),\r
260     MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),\r
261     MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },\r
262   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),\r
263     MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),\r
264     MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },\r
265   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),\r
266     MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),\r
267     MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },\r
268   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),\r
269     MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),\r
270     MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },\r
271   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),\r
272     MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),\r
273     MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },\r
274 };\r
275 \r
276 MyFont *font[NUM_SIZES][NUM_FONTS];\r
277 \r
278 typedef struct {\r
279   char *label;\r
280   int id;\r
281   HWND hwnd;\r
282   WNDPROC wndproc;\r
283 } MyButtonDesc;\r
284 \r
285 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
286 #define N_BUTTONS 5\r
287 \r
288 MyButtonDesc buttonDesc[N_BUTTONS] =\r
289 {\r
290   {"<<", IDM_ToStart, NULL, NULL},\r
291   {"<", IDM_Backward, NULL, NULL},\r
292   {"P", IDM_Pause, NULL, NULL},\r
293   {">", IDM_Forward, NULL, NULL},\r
294   {">>", IDM_ToEnd, NULL, NULL},\r
295 };\r
296 \r
297 int tinyLayout = 0, smallLayout = 0;\r
298 #define MENU_BAR_ITEMS 6\r
299 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
300   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
301   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
302 };\r
303 \r
304 \r
305 MySound sounds[(int)NSoundClasses];\r
306 MyTextAttribs textAttribs[(int)NColorClasses];\r
307 \r
308 MyColorizeAttribs colorizeAttribs[] = {\r
309   { (COLORREF)0, 0, "Shout Text" },\r
310   { (COLORREF)0, 0, "SShout/CShout" },\r
311   { (COLORREF)0, 0, "Channel 1 Text" },\r
312   { (COLORREF)0, 0, "Channel Text" },\r
313   { (COLORREF)0, 0, "Kibitz Text" },\r
314   { (COLORREF)0, 0, "Tell Text" },\r
315   { (COLORREF)0, 0, "Challenge Text" },\r
316   { (COLORREF)0, 0, "Request Text" },\r
317   { (COLORREF)0, 0, "Seek Text" },\r
318   { (COLORREF)0, 0, "Normal Text" },\r
319   { (COLORREF)0, 0, "None" }\r
320 };\r
321 \r
322 \r
323 \r
324 static char *commentTitle;\r
325 static char *commentText;\r
326 static int commentIndex;\r
327 static Boolean editComment = FALSE;\r
328 HWND commentDialog = NULL;\r
329 BOOLEAN commentDialogUp = FALSE;\r
330 static int commentX, commentY, commentH, commentW;\r
331 \r
332 static char *analysisTitle;\r
333 static char *analysisText;\r
334 HWND analysisDialog = NULL;\r
335 BOOLEAN analysisDialogUp = FALSE;\r
336 static int analysisX, analysisY, analysisH, analysisW;\r
337 \r
338 char errorMessage[2*MSG_SIZ];\r
339 HWND errorDialog = NULL;\r
340 BOOLEAN moveErrorMessageUp = FALSE;\r
341 BOOLEAN consoleEcho = TRUE;\r
342 CHARFORMAT consoleCF;\r
343 COLORREF consoleBackgroundColor;\r
344 \r
345 char *programVersion;\r
346 \r
347 #include <winsock.h>\r
348 \r
349 #define CPReal 1\r
350 #define CPComm 2\r
351 #define CPSock 3\r
352 #define CPRcmd 4\r
353 typedef int CPKind;\r
354 \r
355 typedef struct {\r
356   CPKind kind;\r
357   HANDLE hProcess;\r
358   DWORD pid;\r
359   HANDLE hTo;\r
360   HANDLE hFrom;\r
361   SOCKET sock;\r
362   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
363 } ChildProc;\r
364 \r
365 #define INPUT_SOURCE_BUF_SIZE 4096\r
366 \r
367 typedef struct _InputSource {\r
368   CPKind kind;\r
369   HANDLE hFile;\r
370   SOCKET sock;\r
371   int lineByLine;\r
372   HANDLE hThread;\r
373   DWORD id;\r
374   char buf[INPUT_SOURCE_BUF_SIZE];\r
375   char *next;\r
376   DWORD count;\r
377   int error;\r
378   InputCallback func;\r
379   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
380   VOIDSTAR closure;\r
381 } InputSource;\r
382 \r
383 InputSource *consoleInputSource;\r
384 \r
385 DCB dcb;\r
386 \r
387 /* forward */\r
388 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
389 VOID ConsoleCreate();\r
390 LRESULT CALLBACK\r
391   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
392 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
393 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
394 VOID ParseCommSettings(char *arg, DCB *dcb);\r
395 LRESULT CALLBACK\r
396   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
397 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
398 void ParseIcsTextMenu(char *icsTextMenuString);\r
399 VOID PopUpMoveDialog(char firstchar);\r
400 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
401 \r
402 /*\r
403  * Setting "frozen" should disable all user input other than deleting\r
404  * the window.  We do this while engines are initializing themselves.\r
405  */\r
406 static int frozen = 0;\r
407 static int oldMenuItemState[MENU_BAR_ITEMS];\r
408 void FreezeUI()\r
409 {\r
410   HMENU hmenu;\r
411   int i;\r
412 \r
413   if (frozen) return;\r
414   frozen = 1;\r
415   hmenu = GetMenu(hwndMain);\r
416   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
417     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
418   }\r
419   DrawMenuBar(hwndMain);\r
420 }\r
421 \r
422 /* Undo a FreezeUI */\r
423 void ThawUI()\r
424 {\r
425   HMENU hmenu;\r
426   int i;\r
427 \r
428   if (!frozen) return;\r
429   frozen = 0;\r
430   hmenu = GetMenu(hwndMain);\r
431   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
432     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
433   }\r
434   DrawMenuBar(hwndMain);\r
435 }\r
436 \r
437 /*---------------------------------------------------------------------------*\\r
438  *\r
439  * WinMain\r
440  *\r
441 \*---------------------------------------------------------------------------*/\r
442 \r
443 int APIENTRY\r
444 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
445         LPSTR lpCmdLine, int nCmdShow)\r
446 {\r
447   MSG msg;\r
448   HANDLE hAccelMain, hAccelNoAlt;\r
449 \r
450   debugFP = stderr;\r
451 \r
452   LoadLibrary("RICHED32.DLL");\r
453   consoleCF.cbSize = sizeof(CHARFORMAT);\r
454 \r
455   if (!InitApplication(hInstance)) {\r
456     return (FALSE);\r
457   }\r
458   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
459     return (FALSE);\r
460   }\r
461 \r
462   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
463   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
464 \r
465   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
466 \r
467   while (GetMessage(&msg, /* message structure */\r
468                     NULL, /* handle of window receiving the message */\r
469                     0,    /* lowest message to examine */\r
470                     0))   /* highest message to examine */\r
471     {\r
472       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
473           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
474           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
475           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
476           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
477           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
478         TranslateMessage(&msg); /* Translates virtual key codes */\r
479         DispatchMessage(&msg);  /* Dispatches message to window */\r
480       }\r
481     }\r
482 \r
483 \r
484   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
485 }\r
486 \r
487 /*---------------------------------------------------------------------------*\\r
488  *\r
489  * Initialization functions\r
490  *\r
491 \*---------------------------------------------------------------------------*/\r
492 \r
493 BOOL\r
494 InitApplication(HINSTANCE hInstance)\r
495 {\r
496   WNDCLASS wc;\r
497 \r
498   /* Fill in window class structure with parameters that describe the */\r
499   /* main window. */\r
500 \r
501   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
502   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
503   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
504   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
505   wc.hInstance     = hInstance;         /* Owner of this class */\r
506   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
507   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
508   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
509   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
510   wc.lpszClassName = szAppName;                 /* Name to register as */\r
511 \r
512   /* Register the window class and return success/failure code. */\r
513   if (!RegisterClass(&wc)) return FALSE;\r
514 \r
515   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
516   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
517   wc.cbClsExtra    = 0;\r
518   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
519   wc.hInstance     = hInstance;\r
520   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
521   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
522   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
523   wc.lpszMenuName  = NULL;\r
524   wc.lpszClassName = szConsoleName;\r
525 \r
526   if (!RegisterClass(&wc)) return FALSE;\r
527   return TRUE;\r
528 }\r
529 \r
530 \r
531 /* Set by InitInstance, used by EnsureOnScreen */\r
532 int screenHeight, screenWidth;\r
533 \r
534 void\r
535 EnsureOnScreen(int *x, int *y)\r
536 {\r
537   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
538   if (*x > screenWidth - 32) *x = 0;\r
539   if (*y > screenHeight - 32) *y = 0;\r
540 }\r
541 \r
542 BOOL\r
543 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
544 {\r
545   HWND hwnd; /* Main window handle. */\r
546   int ibs;\r
547   WINDOWPLACEMENT wp;\r
548   char *filepart;\r
549 \r
550   hInst = hInstance;    /* Store instance handle in our global variable */\r
551 \r
552   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
553     *filepart = NULLCHAR;\r
554   } else {\r
555     GetCurrentDirectory(MSG_SIZ, installDir);\r
556   }\r
557   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
558   if (appData.debugMode) {\r
559     debugFP = fopen("winboard.debug", "w");\r
560     setbuf(debugFP, NULL);\r
561   }\r
562 \r
563   InitBackEnd1();\r
564 \r
565   /* Create a main window for this application instance. */\r
566   hwnd = CreateWindow(szAppName, szTitle,\r
567                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
568                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
569                       NULL, NULL, hInstance, NULL);\r
570   hwndMain = hwnd;\r
571 \r
572   /* If window could not be created, return "failure" */\r
573   if (!hwnd) {\r
574     return (FALSE);\r
575   }\r
576 \r
577   iconWhite = LoadIcon(hInstance, "icon_white");\r
578   iconBlack = LoadIcon(hInstance, "icon_black");\r
579   iconCurrent = iconWhite;\r
580   InitDrawingColors();\r
581   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
582   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
583   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
584     /* Compute window size for each board size, and use the largest\r
585        size that fits on this screen as the default. */\r
586     InitDrawingSizes((BoardSize)ibs, 0);\r
587     if (boardSize == (BoardSize)-1 &&\r
588         winHeight <= screenHeight && winWidth <= screenWidth) {\r
589       boardSize = (BoardSize)ibs;\r
590     }\r
591   }\r
592   InitDrawingSizes(boardSize, 0);\r
593   InitMenuChecks();\r
594   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
595 \r
596   /* Make a console window if needed */\r
597   if (appData.icsActive) {\r
598     ConsoleCreate();\r
599   }\r
600 \r
601   InitBackEnd2();\r
602 \r
603   /* Make the window visible; update its client area; and return "success" */\r
604   EnsureOnScreen(&boardX, &boardY);\r
605   wp.length = sizeof(WINDOWPLACEMENT);\r
606   wp.flags = 0;\r
607   wp.showCmd = nCmdShow;\r
608   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
609   wp.rcNormalPosition.left = boardX;\r
610   wp.rcNormalPosition.right = boardX + winWidth;\r
611   wp.rcNormalPosition.top = boardY;\r
612   wp.rcNormalPosition.bottom = boardY + winHeight;\r
613   SetWindowPlacement(hwndMain, &wp);\r
614 \r
615   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
616                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
617   if (hwndConsole) {\r
618 #if AOT_CONSOLE\r
619     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
620                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
621 #endif\r
622     ShowWindow(hwndConsole, nCmdShow);\r
623   }\r
624   UpdateWindow(hwnd);\r
625 \r
626   return TRUE;\r
627 \r
628 }\r
629 \r
630 \r
631 typedef enum {\r
632   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
633   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
634   ArgSettingsFilename\r
635 } ArgType;\r
636 \r
637 typedef struct {\r
638   char *argName;\r
639   ArgType argType;\r
640   /***\r
641   union {\r
642     String *pString;       // ArgString\r
643     int *pInt;             // ArgInt\r
644     float *pFloat;         // ArgFloat\r
645     Boolean *pBoolean;     // ArgBoolean\r
646     COLORREF *pColor;      // ArgColor\r
647     ColorClass cc;         // ArgAttribs\r
648     String *pFilename;     // ArgFilename\r
649     BoardSize *pBoardSize; // ArgBoardSize\r
650     int whichFont;         // ArgFont\r
651     DCB *pDCB;             // ArgCommSettings\r
652     String *pFilename;     // ArgSettingsFilename\r
653   } argLoc;\r
654   ***/\r
655   LPVOID argLoc;\r
656   BOOL save;\r
657 } ArgDescriptor;\r
658 \r
659 int junk;\r
660 ArgDescriptor argDescriptors[] = {\r
661   /* positional arguments */\r
662   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
663   { "", ArgNone, NULL },\r
664   /* keyword arguments */\r
665   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
666   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
667   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
668   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
669   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
670   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
671   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
672   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
673   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
674   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
675   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
676   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
677   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
678   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
679   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
680   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
681   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
682   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
683     FALSE },\r
684   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
685     FALSE },\r
686   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
687     FALSE },\r
688   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
689   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
690     FALSE },\r
691   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
692   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
693   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
694   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
695   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
696   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
697   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
698   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
699   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
700   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
701   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
702   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
703   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
704   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
705   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
706   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
707   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
708   /*!!bitmapDirectory?*/\r
709   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
710   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
711   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
712   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
713   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
714   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
715   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
716   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
717   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
718   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
719   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
720   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
721   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
722   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
723   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
724   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
725   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
726   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
727   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
728   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
729   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
730   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
731   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
732   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
733   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
734   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
735   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
736   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
737   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
738   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
739   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
740   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
741   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
742   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
743   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
744   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
745   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
746   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
747   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
748   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
749   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
750   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
751   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
752   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
753   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
754   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
755   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
756   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
757   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
758   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
759   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
760   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
761   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
762   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
763   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
764   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
765   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
766   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
767   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
768   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
769   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
770   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
771   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
772   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
773   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
774   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
775   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
776   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
777   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
778   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
779   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
780   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
781   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
782   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
783   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
784   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
785   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
786   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
787   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
788   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
789   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
790   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
791   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
792   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
793   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
794   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
795   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
796   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
797   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
798   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
799   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
800   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
801   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
802   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
803     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
804   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
805   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
806   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
807   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
808   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
809   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
810   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
811     TRUE }, /* must come after all fonts */\r
812   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
813   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
814     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
815   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
816   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
817   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
818   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
819   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
820   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
821   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
822   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
823   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
824   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
825   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
826   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
827   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
828   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
829   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
830   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
831   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
832   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
833   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
834   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
835   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
836   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
837   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
838   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
839   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
840   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
841   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
842   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
843 #if 0\r
844   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
845   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
846 #endif\r
847   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
848   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
849   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
850   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
851   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
852   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
853   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
854   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
855   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
856   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
857   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
858   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
859   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
860   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
861   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
862   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
863   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
864   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
865   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
866   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
867   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
868   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
869   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
870   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
871   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
872   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
873   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
874   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
875   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
876   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
877   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
878   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
879   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
880   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
881   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
882   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
883   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
884   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
885   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
886   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
887   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
888   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
889   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
890   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
891   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
892   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
893   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
894   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
895   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
896   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
897   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
898   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
899   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
900   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
901   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
902   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
903   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
904   { "highlightLastMove", ArgBoolean,\r
905     (LPVOID) &appData.highlightLastMove, TRUE },\r
906   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
907   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
908   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
909   { "highlightDragging", ArgBoolean,\r
910     (LPVOID) &appData.highlightDragging, TRUE },\r
911   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
912   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
913   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
914   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
915   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
916   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
917   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
918   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
919   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
920   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
921   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
922   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
923   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
924   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
925   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
926   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
927   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
928   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
929   { "soundShout", ArgFilename,\r
930     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
931   { "soundSShout", ArgFilename,\r
932     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
933   { "soundChannel1", ArgFilename,\r
934     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
935   { "soundChannel", ArgFilename,\r
936     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
937   { "soundKibitz", ArgFilename,\r
938     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
939   { "soundTell", ArgFilename,\r
940     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
941   { "soundChallenge", ArgFilename,\r
942     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
943   { "soundRequest", ArgFilename,\r
944     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
945   { "soundSeek", ArgFilename,\r
946     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
947   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
948   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
949   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
950   { "soundIcsLoss", ArgFilename, \r
951     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
952   { "soundIcsDraw", ArgFilename, \r
953     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
954   { "soundIcsUnfinished", ArgFilename, \r
955     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
956   { "soundIcsAlarm", ArgFilename, \r
957     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
958   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
959   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
960   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
961   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
962   { "reuseChessPrograms", ArgBoolean,\r
963     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
964   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
965   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
966   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
967   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
968   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
969   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
970   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
971   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },\r
972   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },\r
973   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },\r
974   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },\r
975   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },\r
976   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },\r
977   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },\r
978   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },\r
979   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },\r
980   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },\r
981   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
982   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
983   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },\r
984   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },\r
985   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
986   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
987   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },\r
988   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },\r
989   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },\r
990   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },\r
991   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
992   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
993   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
994   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
995   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
996   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
997   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
998   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
999   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1000   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1001     TRUE },\r
1002   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1003     TRUE },\r
1004   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1005   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1006   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1007   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,\r
1008     FALSE },\r
1009   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,\r
1010     FALSE },\r
1011   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1012   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1013   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1014   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1015 #ifdef ZIPPY\r
1016   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1017   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1018   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1019   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1020   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1021   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1022   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1023   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1024   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1025   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1026   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1027   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1028   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1029     FALSE },\r
1030   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1031   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1032   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1033   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1034   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1035   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1036   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1037     FALSE },\r
1038   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1039   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1040   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1041   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1042   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1043   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1044   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1045   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1046   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1047   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1048   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1049   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1050   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1051   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1052   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1053   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1054   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1055   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1056 #endif\r
1057   { NULL, ArgNone, NULL, FALSE }\r
1058 };\r
1059 \r
1060 \r
1061 /* Kludge for indirection files on command line */\r
1062 char* lastIndirectionFilename;\r
1063 ArgDescriptor argDescriptorIndirection =\r
1064 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1065 \r
1066 \r
1067 VOID\r
1068 ExitArgError(char *msg, char *badArg)\r
1069 {\r
1070   char buf[MSG_SIZ];\r
1071 \r
1072   sprintf(buf, "%s %s", msg, badArg);\r
1073   DisplayFatalError(buf, 0, 2);\r
1074   exit(2);\r
1075 }\r
1076 \r
1077 /* Command line font name parser.  NULL name means do nothing.\r
1078    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1079    For backward compatibility, syntax without the colon is also\r
1080    accepted, but font names with digits in them won't work in that case.\r
1081 */\r
1082 VOID\r
1083 ParseFontName(char *name, MyFontParams *mfp)\r
1084 {\r
1085   char *p, *q;\r
1086   if (name == NULL) return;\r
1087   p = name;\r
1088   q = strchr(p, ':');\r
1089   if (q) {\r
1090     if (q - p >= sizeof(mfp->faceName))\r
1091       ExitArgError("Font name too long:", name);\r
1092     memcpy(mfp->faceName, p, q - p);\r
1093     mfp->faceName[q - p] = NULLCHAR;\r
1094     p = q + 1;\r
1095   } else {\r
1096     q = mfp->faceName;\r
1097     while (*p && !isdigit(*p)) {\r
1098       *q++ = *p++;\r
1099       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1100         ExitArgError("Font name too long:", name);\r
1101     }\r
1102     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1103     *q = NULLCHAR;\r
1104   }\r
1105   if (!*p) ExitArgError("Font point size missing:", name);\r
1106   mfp->pointSize = (float) atof(p);\r
1107   mfp->bold = (strchr(p, 'b') != NULL);\r
1108   mfp->italic = (strchr(p, 'i') != NULL);\r
1109   mfp->underline = (strchr(p, 'u') != NULL);\r
1110   mfp->strikeout = (strchr(p, 's') != NULL);\r
1111 }\r
1112 \r
1113 /* Color name parser.\r
1114    X version accepts X color names, but this one\r
1115    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1116 COLORREF\r
1117 ParseColorName(char *name)\r
1118 {\r
1119   int red, green, blue, count;\r
1120   char buf[MSG_SIZ];\r
1121 \r
1122   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1123   if (count != 3) {\r
1124     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1125       &red, &green, &blue);\r
1126   }\r
1127   if (count != 3) {\r
1128     sprintf(buf, "Can't parse color name %s", name);\r
1129     DisplayError(buf, 0);\r
1130     return RGB(0, 0, 0);\r
1131   }\r
1132   return PALETTERGB(red, green, blue);\r
1133 }\r
1134 \r
1135 \r
1136 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1137 {\r
1138   char *e = argValue;\r
1139   int eff = 0;\r
1140 \r
1141   while (*e) {\r
1142     if (*e == 'b')      eff |= CFE_BOLD;\r
1143     else if (*e == 'i') eff |= CFE_ITALIC;\r
1144     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1145     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1146     else if (*e == '#' || isdigit(*e)) break;\r
1147     e++;\r
1148   }\r
1149   *effects = eff;\r
1150   *color   = ParseColorName(e);\r
1151 }\r
1152 \r
1153 \r
1154 BoardSize\r
1155 ParseBoardSize(char *name)\r
1156 {\r
1157   BoardSize bs = SizeTiny;\r
1158   while (sizeInfo[bs].name != NULL) {\r
1159     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1160     bs++;\r
1161   }\r
1162   ExitArgError("Unrecognized board size value", name);\r
1163   return bs; /* not reached */\r
1164 }\r
1165 \r
1166 \r
1167 char\r
1168 StringGet(void *getClosure)\r
1169 {\r
1170   char **p = (char **) getClosure;\r
1171   return *((*p)++);\r
1172 }\r
1173 \r
1174 char\r
1175 FileGet(void *getClosure)\r
1176 {\r
1177   int c;\r
1178   FILE* f = (FILE*) getClosure;\r
1179 \r
1180   c = getc(f);\r
1181   if (c == EOF)\r
1182     return NULLCHAR;\r
1183   else\r
1184     return (char) c;\r
1185 }\r
1186 \r
1187 /* Parse settings file named "name". If file found, return the\r
1188    full name in fullname and return TRUE; else return FALSE */\r
1189 BOOLEAN\r
1190 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1191 {\r
1192   char *dummy;\r
1193   FILE *f;\r
1194 \r
1195   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {\r
1196     f = fopen(fullname, "r");\r
1197     if (f != NULL) {\r
1198       ParseArgs(FileGet, f);\r
1199       fclose(f);\r
1200       return TRUE;\r
1201     }\r
1202   }\r
1203   return FALSE;\r
1204 }\r
1205 \r
1206 VOID\r
1207 ParseArgs(GetFunc get, void *cl)\r
1208 {\r
1209   char argName[ARG_MAX];\r
1210   char argValue[ARG_MAX];\r
1211   ArgDescriptor *ad;\r
1212   char start;\r
1213   char *q;\r
1214   int i, octval;\r
1215   char ch;\r
1216   int posarg = 0;\r
1217 \r
1218   ch = get(cl);\r
1219   for (;;) {\r
1220     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1221     if (ch == NULLCHAR) break;\r
1222     if (ch == ';') {\r
1223       /* Comment to end of line */\r
1224       ch = get(cl);\r
1225       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1226       continue;\r
1227     } else if (ch == '/' || ch == '-') {\r
1228       /* Switch */\r
1229       q = argName;\r
1230       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1231              ch != '\n' && ch != '\t') {\r
1232         *q++ = ch;\r
1233         ch = get(cl);\r
1234       }\r
1235       *q = NULLCHAR;\r
1236 \r
1237       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1238         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1239 \r
1240       if (ad->argName == NULL)\r
1241         ExitArgError("Unrecognized argument", argName);\r
1242 \r
1243     } else if (ch == '@') {\r
1244       /* Indirection file */\r
1245       ad = &argDescriptorIndirection;\r
1246       ch = get(cl);\r
1247     } else {\r
1248       /* Positional argument */\r
1249       ad = &argDescriptors[posarg++];\r
1250       strcpy(argName, ad->argName);\r
1251     }\r
1252 \r
1253     if (ad->argType == ArgTrue) {\r
1254       *(Boolean *) ad->argLoc = TRUE;\r
1255       continue;\r
1256     }\r
1257     if (ad->argType == ArgFalse) {\r
1258       *(Boolean *) ad->argLoc = FALSE;\r
1259       continue;\r
1260     }\r
1261 \r
1262     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1263     if (ch == NULLCHAR || ch == '\n') {\r
1264       ExitArgError("No value provided for argument", argName);\r
1265     }\r
1266     q = argValue;\r
1267     if (ch == '{') {\r
1268       // Quoting with { }.  No characters have to (or can) be escaped.\r
1269       // Thus the string cannot contain a '}' character.\r
1270       start = ch;\r
1271       ch = get(cl);\r
1272       while (start) {\r
1273         switch (ch) {\r
1274         case NULLCHAR:\r
1275           start = NULLCHAR;\r
1276           break;\r
1277           \r
1278         case '}':\r
1279           ch = get(cl);\r
1280           start = NULLCHAR;\r
1281           break;\r
1282 \r
1283         default:\r
1284           *q++ = ch;\r
1285           ch = get(cl);\r
1286           break;\r
1287         }\r
1288       }   \r
1289     } else if (ch == '\'' || ch == '"') {\r
1290       // Quoting with ' ' or " ", with \ as escape character.\r
1291       // Inconvenient for long strings that may contain Windows filenames.\r
1292       start = ch;\r
1293       ch = get(cl);\r
1294       while (start) {\r
1295         switch (ch) {\r
1296         case NULLCHAR:\r
1297           start = NULLCHAR;\r
1298           break;\r
1299 \r
1300         default:\r
1301         not_special:\r
1302           *q++ = ch;\r
1303           ch = get(cl);\r
1304           break;\r
1305 \r
1306         case '\'':\r
1307         case '\"':\r
1308           if (ch == start) {\r
1309             ch = get(cl);\r
1310             start = NULLCHAR;\r
1311             break;\r
1312           } else {\r
1313             goto not_special;\r
1314           }\r
1315 \r
1316         case '\\':\r
1317           if (ad->argType == ArgFilename\r
1318               || ad->argType == ArgSettingsFilename) {\r
1319               goto not_special;\r
1320           }\r
1321           ch = get(cl);\r
1322           switch (ch) {\r
1323           case NULLCHAR:\r
1324             ExitArgError("Incomplete \\ escape in value for", argName);\r
1325             break;\r
1326           case 'n':\r
1327             *q++ = '\n';\r
1328             ch = get(cl);\r
1329             break;\r
1330           case 'r':\r
1331             *q++ = '\r';\r
1332             ch = get(cl);\r
1333             break;\r
1334           case 't':\r
1335             *q++ = '\t';\r
1336             ch = get(cl);\r
1337             break;\r
1338           case 'b':\r
1339             *q++ = '\b';\r
1340             ch = get(cl);\r
1341             break;\r
1342           case 'f':\r
1343             *q++ = '\f';\r
1344             ch = get(cl);\r
1345             break;\r
1346           default:\r
1347             octval = 0;\r
1348             for (i = 0; i < 3; i++) {\r
1349               if (ch >= '0' && ch <= '7') {\r
1350                 octval = octval*8 + (ch - '0');\r
1351                 ch = get(cl);\r
1352               } else {\r
1353                 break;\r
1354               }\r
1355             }\r
1356             if (i > 0) {\r
1357               *q++ = (char) octval;\r
1358             } else {\r
1359               *q++ = ch;\r
1360               ch = get(cl);\r
1361             }\r
1362             break;\r
1363           }\r
1364           break;\r
1365         }\r
1366       }\r
1367     } else {\r
1368       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1369         *q++ = ch;\r
1370         ch = get(cl);\r
1371       }\r
1372     }\r
1373     *q = NULLCHAR;\r
1374 \r
1375     switch (ad->argType) {\r
1376     case ArgInt:\r
1377       *(int *) ad->argLoc = atoi(argValue);\r
1378       break;\r
1379 \r
1380     case ArgFloat:\r
1381       *(float *) ad->argLoc = (float) atof(argValue);\r
1382       break;\r
1383 \r
1384     case ArgString:\r
1385     case ArgFilename:\r
1386       *(char **) ad->argLoc = strdup(argValue);\r
1387       break;\r
1388 \r
1389     case ArgSettingsFilename:\r
1390       {\r
1391         char fullname[MSG_SIZ];\r
1392         if (ParseSettingsFile(argValue, fullname)) {\r
1393           if (ad->argLoc != NULL) {\r
1394             *(char **) ad->argLoc = strdup(fullname);\r
1395           }\r
1396         } else {\r
1397           if (ad->argLoc != NULL) {\r
1398           } else {\r
1399             ExitArgError("Failed to open indirection file", argValue);\r
1400           }\r
1401         }\r
1402       }\r
1403       break;\r
1404 \r
1405     case ArgBoolean:\r
1406       switch (argValue[0]) {\r
1407       case 't':\r
1408       case 'T':\r
1409         *(Boolean *) ad->argLoc = TRUE;\r
1410         break;\r
1411       case 'f':\r
1412       case 'F':\r
1413         *(Boolean *) ad->argLoc = FALSE;\r
1414         break;\r
1415       default:\r
1416         ExitArgError("Unrecognized boolean argument value", argValue);\r
1417         break;\r
1418       }\r
1419       break;\r
1420 \r
1421     case ArgColor:\r
1422       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1423       break;\r
1424 \r
1425     case ArgAttribs: {\r
1426       ColorClass cc = (ColorClass)ad->argLoc;\r
1427       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1428       }\r
1429       break;\r
1430       \r
1431     case ArgBoardSize:\r
1432       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1433       break;\r
1434 \r
1435     case ArgFont:\r
1436       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1437       break;\r
1438 \r
1439     case ArgCommSettings:\r
1440       ParseCommSettings(argValue, &dcb);\r
1441       break;\r
1442 \r
1443     case ArgNone:\r
1444       ExitArgError("Unrecognized argument", argValue);\r
1445       break;\r
1446     }\r
1447   }\r
1448 }\r
1449 \r
1450 VOID\r
1451 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1452 {\r
1453   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1454   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1455   DeleteDC(hdc);\r
1456   lf->lfWidth = 0;\r
1457   lf->lfEscapement = 0;\r
1458   lf->lfOrientation = 0;\r
1459   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1460   lf->lfItalic = mfp->italic;\r
1461   lf->lfUnderline = mfp->underline;\r
1462   lf->lfStrikeOut = mfp->strikeout;\r
1463   lf->lfCharSet = DEFAULT_CHARSET;\r
1464   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1465   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1466   lf->lfQuality = DEFAULT_QUALITY;\r
1467   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1468   strcpy(lf->lfFaceName, mfp->faceName);\r
1469 }\r
1470 \r
1471 VOID\r
1472 CreateFontInMF(MyFont *mf)\r
1473 {\r
1474   LFfromMFP(&mf->lf, &mf->mfp);\r
1475   if (mf->hf) DeleteObject(mf->hf);\r
1476   mf->hf = CreateFontIndirect(&mf->lf);\r
1477 }\r
1478 \r
1479 VOID\r
1480 SetDefaultTextAttribs()\r
1481 {\r
1482   ColorClass cc;\r
1483   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1484     ParseAttribs(&textAttribs[cc].color, \r
1485                  &textAttribs[cc].effects, \r
1486                  defaultTextAttribs[cc]);\r
1487   }\r
1488 }\r
1489 \r
1490 VOID\r
1491 SetDefaultSounds()\r
1492 {\r
1493   ColorClass cc;\r
1494   SoundClass sc;\r
1495   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1496     textAttribs[cc].sound.name = strdup("");\r
1497     textAttribs[cc].sound.data = NULL;\r
1498   }\r
1499   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1500     sounds[sc].name = strdup("");\r
1501     sounds[sc].data = NULL;\r
1502   }\r
1503   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1504 }\r
1505 \r
1506 VOID\r
1507 LoadAllSounds()\r
1508 {\r
1509   ColorClass cc;\r
1510   SoundClass sc;\r
1511   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1512     MyLoadSound(&textAttribs[cc].sound);\r
1513   }\r
1514   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1515     MyLoadSound(&sounds[sc]);\r
1516   }\r
1517 }\r
1518 \r
1519 VOID\r
1520 InitAppData(LPSTR lpCmdLine)\r
1521 {\r
1522   int i, j;\r
1523   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1524   char *dummy, *p;\r
1525 \r
1526   programName = szAppName;\r
1527 \r
1528   /* Initialize to defaults */\r
1529   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1530   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1531   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1532   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1533   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1534   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1535   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1536   SetDefaultTextAttribs();\r
1537   SetDefaultSounds();\r
1538   appData.movesPerSession = MOVES_PER_SESSION;\r
1539   appData.initString = INIT_STRING;\r
1540   appData.secondInitString = INIT_STRING;\r
1541   appData.firstComputerString = COMPUTER_STRING;\r
1542   appData.secondComputerString = COMPUTER_STRING;\r
1543   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1544   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1545   appData.firstPlaysBlack = FALSE;\r
1546   appData.noChessProgram = FALSE;\r
1547   chessProgram = FALSE;\r
1548   appData.firstHost = FIRST_HOST;\r
1549   appData.secondHost = SECOND_HOST;\r
1550   appData.firstDirectory = FIRST_DIRECTORY;\r
1551   appData.secondDirectory = SECOND_DIRECTORY;\r
1552   appData.bitmapDirectory = "";\r
1553   appData.remoteShell = REMOTE_SHELL;\r
1554   appData.remoteUser = "";\r
1555   appData.timeDelay = TIME_DELAY;\r
1556   appData.timeControl = TIME_CONTROL;\r
1557   appData.timeIncrement = TIME_INCREMENT;\r
1558   appData.icsActive = FALSE;\r
1559   appData.icsHost = "";\r
1560   appData.icsPort = ICS_PORT;\r
1561   appData.icsCommPort = ICS_COMM_PORT;\r
1562   appData.icsLogon = ICS_LOGON;\r
1563   appData.icsHelper = "";\r
1564   appData.useTelnet = FALSE;\r
1565   appData.telnetProgram = TELNET_PROGRAM;\r
1566   appData.gateway = "";\r
1567   appData.loadGameFile = "";\r
1568   appData.loadGameIndex = 0;\r
1569   appData.saveGameFile = "";\r
1570   appData.autoSaveGames = FALSE;\r
1571   appData.loadPositionFile = "";\r
1572   appData.loadPositionIndex = 1;\r
1573   appData.savePositionFile = "";\r
1574   appData.matchMode = FALSE;\r
1575   appData.matchGames = 0;\r
1576   appData.monoMode = FALSE;\r
1577   appData.debugMode = FALSE;\r
1578   appData.clockMode = TRUE;\r
1579   boardSize = (BoardSize) -1; /* determine by screen size */\r
1580   appData.Iconic = FALSE; /*unused*/\r
1581   appData.searchTime = "";\r
1582   appData.searchDepth = 0;\r
1583   appData.showCoords = FALSE;\r
1584   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1585   appData.autoCallFlag = FALSE;\r
1586   appData.flipView = FALSE;\r
1587   appData.autoFlipView = TRUE;\r
1588   appData.cmailGameName = "";\r
1589   appData.alwaysPromoteToQueen = FALSE;\r
1590   appData.oldSaveStyle = FALSE;\r
1591   appData.quietPlay = FALSE;\r
1592   appData.showThinking = FALSE;\r
1593   appData.ponderNextMove = TRUE;\r
1594   appData.periodicUpdates = TRUE;\r
1595   appData.popupExitMessage = TRUE;\r
1596   appData.popupMoveErrors = FALSE;\r
1597   appData.autoObserve = FALSE;\r
1598   appData.autoComment = FALSE;\r
1599   appData.animate = TRUE;\r
1600   appData.animSpeed = 10;\r
1601   appData.animateDragging = TRUE;\r
1602   appData.highlightLastMove = TRUE;\r
1603   appData.getMoveList = TRUE;\r
1604   appData.testLegality = TRUE;\r
1605   appData.premove = TRUE;\r
1606   appData.premoveWhite = FALSE;\r
1607   appData.premoveWhiteText = "";\r
1608   appData.premoveBlack = FALSE;\r
1609   appData.premoveBlackText = "";\r
1610   appData.icsAlarm = TRUE;\r
1611   appData.icsAlarmTime = 5000;\r
1612   appData.autoRaiseBoard = TRUE;\r
1613   appData.localLineEditing = TRUE;\r
1614   appData.colorize = TRUE;\r
1615   appData.reuseFirst = TRUE;\r
1616   appData.reuseSecond = TRUE;\r
1617   appData.blindfold = FALSE;\r
1618   dcb.DCBlength = sizeof(DCB);\r
1619   dcb.BaudRate = 9600;\r
1620   dcb.fBinary = TRUE;\r
1621   dcb.fParity = FALSE;\r
1622   dcb.fOutxCtsFlow = FALSE;\r
1623   dcb.fOutxDsrFlow = FALSE;\r
1624   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
1625   dcb.fDsrSensitivity = FALSE;\r
1626   dcb.fTXContinueOnXoff = TRUE;\r
1627   dcb.fOutX = FALSE;\r
1628   dcb.fInX = FALSE;\r
1629   dcb.fNull = FALSE;\r
1630   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
1631   dcb.fAbortOnError = FALSE;\r
1632   dcb.wReserved = 0;\r
1633   dcb.ByteSize = 7;\r
1634   dcb.Parity = SPACEPARITY;\r
1635   dcb.StopBits = ONESTOPBIT;\r
1636   settingsFileName = SETTINGS_FILE;\r
1637   saveSettingsOnExit = TRUE;\r
1638   boardX = CW_USEDEFAULT;\r
1639   boardY = CW_USEDEFAULT;\r
1640   consoleX = CW_USEDEFAULT; \r
1641   consoleY = CW_USEDEFAULT; \r
1642   consoleW = CW_USEDEFAULT;\r
1643   consoleH = CW_USEDEFAULT;\r
1644   analysisX = CW_USEDEFAULT; \r
1645   analysisY = CW_USEDEFAULT; \r
1646   analysisW = CW_USEDEFAULT;\r
1647   analysisH = CW_USEDEFAULT;\r
1648   commentX = CW_USEDEFAULT; \r
1649   commentY = CW_USEDEFAULT; \r
1650   commentW = CW_USEDEFAULT;\r
1651   commentH = CW_USEDEFAULT;\r
1652   editTagsX = CW_USEDEFAULT; \r
1653   editTagsY = CW_USEDEFAULT; \r
1654   editTagsW = CW_USEDEFAULT;\r
1655   editTagsH = CW_USEDEFAULT;\r
1656   gameListX = CW_USEDEFAULT; \r
1657   gameListY = CW_USEDEFAULT; \r
1658   gameListW = CW_USEDEFAULT;\r
1659   gameListH = CW_USEDEFAULT;\r
1660   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
1661   icsNames = ICS_NAMES;\r
1662   firstChessProgramNames = FCP_NAMES;\r
1663   secondChessProgramNames = SCP_NAMES;\r
1664   appData.initialMode = "";\r
1665   appData.variant = "normal";\r
1666   appData.firstProtocolVersion = PROTOVER;\r
1667   appData.secondProtocolVersion = PROTOVER;\r
1668   appData.showButtonBar = TRUE;\r
1669 #ifdef ZIPPY\r
1670   appData.zippyTalk = ZIPPY_TALK;\r
1671   appData.zippyPlay = ZIPPY_PLAY;\r
1672   appData.zippyLines = ZIPPY_LINES;\r
1673   appData.zippyPinhead = ZIPPY_PINHEAD;\r
1674   appData.zippyPassword = ZIPPY_PASSWORD;\r
1675   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
1676   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
1677   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
1678   appData.zippyUseI = ZIPPY_USE_I;\r
1679   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
1680   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
1681   appData.zippyGameEnd = ZIPPY_GAME_END;\r
1682   appData.zippyGameStart = ZIPPY_GAME_START;\r
1683   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
1684   appData.zippyAbort = ZIPPY_ABORT;\r
1685   appData.zippyVariants = ZIPPY_VARIANTS;\r
1686   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
1687   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
1688 #endif\r
1689 \r
1690   /* Point font array elements to structures and\r
1691      parse default font names */\r
1692   for (i=0; i<NUM_FONTS; i++) {\r
1693     for (j=0; j<NUM_SIZES; j++) {\r
1694       font[j][i] = &fontRec[j][i];\r
1695       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
1696     }\r
1697   }\r
1698   \r
1699   /* Parse default settings file if any */\r
1700   if (ParseSettingsFile(settingsFileName, buf)) {\r
1701     settingsFileName = strdup(buf);\r
1702   }\r
1703 \r
1704   /* Parse command line */\r
1705   ParseArgs(StringGet, &lpCmdLine);\r
1706 \r
1707   /* Propagate options that affect others */\r
1708   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
1709   if (appData.icsActive || appData.noChessProgram) {\r
1710      chessProgram = FALSE;  /* not local chess program mode */\r
1711   }\r
1712 \r
1713   /* Open startup dialog if needed */\r
1714   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
1715       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
1716       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
1717                         *appData.secondChessProgram == NULLCHAR))) {\r
1718     FARPROC lpProc;\r
1719     \r
1720     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
1721     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
1722     FreeProcInstance(lpProc);\r
1723   }\r
1724 \r
1725   /* Make sure save files land in the right (?) directory */\r
1726   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
1727     appData.saveGameFile = strdup(buf);\r
1728   }\r
1729   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
1730     appData.savePositionFile = strdup(buf);\r
1731   }\r
1732 \r
1733   /* Finish initialization for fonts and sounds */\r
1734   for (i=0; i<NUM_FONTS; i++) {\r
1735     for (j=0; j<NUM_SIZES; j++) {\r
1736       CreateFontInMF(font[j][i]);\r
1737     }\r
1738   }\r
1739   /* xboard, and older WinBoards, controlled the move sound with the\r
1740      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
1741      always turn the option on (so that the backend will call us),\r
1742      then let the user turn the sound off by setting it to silence if\r
1743      desired.  To accommodate old winboard.ini files saved by old\r
1744      versions of WinBoard, we also turn off the sound if the option\r
1745      was initially set to false. */\r
1746   if (!appData.ringBellAfterMoves) {\r
1747     sounds[(int)SoundMove].name = strdup("");\r
1748     appData.ringBellAfterMoves = TRUE;\r
1749   }\r
1750   GetCurrentDirectory(MSG_SIZ, currDir);\r
1751   SetCurrentDirectory(installDir);\r
1752   LoadAllSounds();\r
1753   SetCurrentDirectory(currDir);\r
1754 \r
1755   p = icsTextMenuString;\r
1756   if (p[0] == '@') {\r
1757     FILE* f = fopen(p + 1, "r");\r
1758     if (f == NULL) {\r
1759       DisplayFatalError(p + 1, errno, 2);\r
1760       return;\r
1761     }\r
1762     i = fread(buf, 1, sizeof(buf)-1, f);\r
1763     fclose(f);\r
1764     buf[i] = NULLCHAR;\r
1765     p = buf;\r
1766   }\r
1767   ParseIcsTextMenu(strdup(p));\r
1768 }\r
1769 \r
1770 \r
1771 VOID\r
1772 InitMenuChecks()\r
1773 {\r
1774   HMENU hmenu = GetMenu(hwndMain);\r
1775 \r
1776   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
1777                         MF_BYCOMMAND|((appData.icsActive &&\r
1778                                        *appData.icsCommPort != NULLCHAR) ?\r
1779                                       MF_ENABLED : MF_GRAYED));\r
1780   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
1781                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
1782                                      MF_CHECKED : MF_UNCHECKED));\r
1783 }\r
1784 \r
1785 \r
1786 VOID\r
1787 SaveSettings(char* name)\r
1788 {\r
1789   FILE *f;\r
1790   ArgDescriptor *ad;\r
1791   WINDOWPLACEMENT wp;\r
1792   char dir[MSG_SIZ];\r
1793 \r
1794   if (!hwndMain) return;\r
1795 \r
1796   GetCurrentDirectory(MSG_SIZ, dir);\r
1797   SetCurrentDirectory(installDir);\r
1798   f = fopen(name, "w");\r
1799   SetCurrentDirectory(dir);\r
1800   if (f == NULL) {\r
1801     DisplayError(name, errno);\r
1802     return;\r
1803   }\r
1804   fprintf(f, ";\n");\r
1805   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
1806   fprintf(f, ";\n");\r
1807   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
1808   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
1809   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
1810   fprintf(f, ";\n");\r
1811 \r
1812   wp.length = sizeof(WINDOWPLACEMENT);\r
1813   GetWindowPlacement(hwndMain, &wp);\r
1814   boardX = wp.rcNormalPosition.left;\r
1815   boardY = wp.rcNormalPosition.top;\r
1816 \r
1817   if (hwndConsole) {\r
1818     GetWindowPlacement(hwndConsole, &wp);\r
1819     consoleX = wp.rcNormalPosition.left;\r
1820     consoleY = wp.rcNormalPosition.top;\r
1821     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1822     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1823   }\r
1824 \r
1825   if (analysisDialog) {\r
1826     GetWindowPlacement(analysisDialog, &wp);\r
1827     analysisX = wp.rcNormalPosition.left;\r
1828     analysisY = wp.rcNormalPosition.top;\r
1829     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1830     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1831   }\r
1832 \r
1833   if (commentDialog) {\r
1834     GetWindowPlacement(commentDialog, &wp);\r
1835     commentX = wp.rcNormalPosition.left;\r
1836     commentY = wp.rcNormalPosition.top;\r
1837     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1838     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1839   }\r
1840 \r
1841   if (editTagsDialog) {\r
1842     GetWindowPlacement(editTagsDialog, &wp);\r
1843     editTagsX = wp.rcNormalPosition.left;\r
1844     editTagsY = wp.rcNormalPosition.top;\r
1845     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1846     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1847   }\r
1848 \r
1849   if (gameListDialog) {\r
1850     GetWindowPlacement(gameListDialog, &wp);\r
1851     gameListX = wp.rcNormalPosition.left;\r
1852     gameListY = wp.rcNormalPosition.top;\r
1853     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
1854     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
1855   }\r
1856 \r
1857   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
1858     if (!ad->save) continue;\r
1859     switch (ad->argType) {\r
1860     case ArgString:\r
1861       {\r
1862         char *p = *(char **)ad->argLoc;\r
1863         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
1864           /* Quote multiline values or \-containing values\r
1865              with { } if possible */\r
1866           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
1867         } else {\r
1868           /* Else quote with " " */\r
1869           fprintf(f, "/%s=\"", ad->argName);\r
1870           while (*p) {\r
1871             if (*p == '\n') fprintf(f, "\n");\r
1872             else if (*p == '\r') fprintf(f, "\\r");\r
1873             else if (*p == '\t') fprintf(f, "\\t");\r
1874             else if (*p == '\b') fprintf(f, "\\b");\r
1875             else if (*p == '\f') fprintf(f, "\\f");\r
1876             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
1877             else if (*p == '\"') fprintf(f, "\\\"");\r
1878             else if (*p == '\\') fprintf(f, "\\\\");\r
1879             else putc(*p, f);\r
1880             p++;\r
1881           }\r
1882           fprintf(f, "\"\n");\r
1883         }\r
1884       }\r
1885       break;\r
1886     case ArgInt:\r
1887       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
1888       break;\r
1889     case ArgFloat:\r
1890       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
1891       break;\r
1892     case ArgBoolean:\r
1893       fprintf(f, "/%s=%s\n", ad->argName, \r
1894         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
1895       break;\r
1896     case ArgTrue:\r
1897       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
1898       break;\r
1899     case ArgFalse:\r
1900       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
1901       break;\r
1902     case ArgColor:\r
1903       {\r
1904         COLORREF color = *(COLORREF *)ad->argLoc;\r
1905         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, \r
1906           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
1907       }\r
1908       break;\r
1909     case ArgAttribs:\r
1910       {\r
1911         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
1912         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,\r
1913           (ta->effects & CFE_BOLD) ? "b" : "",\r
1914           (ta->effects & CFE_ITALIC) ? "i" : "",\r
1915           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
1916           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
1917           (ta->effects) ? " " : "",\r
1918           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
1919       }\r
1920       break;\r
1921     case ArgFilename:\r
1922       if (strchr(*(char **)ad->argLoc, '\"')) {\r
1923         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
1924       } else {\r
1925         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
1926       }\r
1927       break;\r
1928     case ArgBoardSize:\r
1929       fprintf(f, "/%s=%s\n", ad->argName,\r
1930               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
1931       break;\r
1932     case ArgFont:\r
1933       {\r
1934         int bs;\r
1935         for (bs=0; bs<NUM_SIZES; bs++) {\r
1936           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
1937           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
1938           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
1939             ad->argName, mfp->faceName, mfp->pointSize,\r
1940             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
1941             mfp->bold ? "b" : "",\r
1942             mfp->italic ? "i" : "",\r
1943             mfp->underline ? "u" : "",\r
1944             mfp->strikeout ? "s" : "");\r
1945         }\r
1946       }\r
1947       break;\r
1948     case ArgCommSettings:\r
1949       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
1950     }\r
1951   }\r
1952   fclose(f);\r
1953 }\r
1954 \r
1955 \r
1956 \r
1957 /*---------------------------------------------------------------------------*\\r
1958  *\r
1959  * GDI board drawing routines\r
1960  *\r
1961 \*---------------------------------------------------------------------------*/\r
1962 \r
1963 HBITMAP\r
1964 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
1965 {\r
1966   char name[128];\r
1967 \r
1968   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
1969   if (gameInfo.event &&\r
1970       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
1971       strcmp(name, "k80s") == 0) {\r
1972     strcpy(name, "tim");\r
1973   }\r
1974   return LoadBitmap(hinst, name);\r
1975 }\r
1976 \r
1977 \r
1978 /* Insert a color into the program's logical palette\r
1979    structure.  This code assumes the given color is\r
1980    the result of the RGB or PALETTERGB macro, and it\r
1981    knows how those macros work (which is documented).\r
1982 */\r
1983 VOID\r
1984 InsertInPalette(COLORREF color)\r
1985 {\r
1986   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
1987 \r
1988   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
1989     DisplayFatalError("Too many colors", 0, 1);\r
1990     pLogPal->palNumEntries--;\r
1991     return;\r
1992   }\r
1993 \r
1994   pe->peFlags = (char) 0;\r
1995   pe->peRed = (char) (0xFF & color);\r
1996   pe->peGreen = (char) (0xFF & (color >> 8));\r
1997   pe->peBlue = (char) (0xFF & (color >> 16));\r
1998   return;\r
1999 }\r
2000 \r
2001 \r
2002 VOID\r
2003 InitDrawingColors()\r
2004 {\r
2005   if (pLogPal == NULL) {\r
2006     /* Allocate enough memory for a logical palette with\r
2007      * PALETTESIZE entries and set the size and version fields\r
2008      * of the logical palette structure.\r
2009      */\r
2010     pLogPal = (NPLOGPALETTE)\r
2011       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
2012                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
2013     pLogPal->palVersion    = 0x300;\r
2014   }\r
2015   pLogPal->palNumEntries = 0;\r
2016 \r
2017   InsertInPalette(lightSquareColor);\r
2018   InsertInPalette(darkSquareColor);\r
2019   InsertInPalette(whitePieceColor);\r
2020   InsertInPalette(blackPieceColor);\r
2021   InsertInPalette(highlightSquareColor);\r
2022   InsertInPalette(premoveHighlightColor);\r
2023 \r
2024   /*  create a logical color palette according the information\r
2025    *  in the LOGPALETTE structure.\r
2026    */\r
2027   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
2028 \r
2029   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
2030   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
2031   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
2032   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
2033   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
2034 }\r
2035 \r
2036 \r
2037 int\r
2038 BoardWidth(int boardSize)\r
2039 {\r
2040   return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +\r
2041           BOARD_SIZE * sizeInfo[boardSize].squareSize;\r
2042 }\r
2043 \r
2044 /* Respond to board resize by dragging edge */\r
2045 VOID\r
2046 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
2047 {\r
2048   BoardSize newSize = NUM_SIZES - 1;\r
2049   static int recurse = 0;\r
2050   if (IsIconic(hwndMain)) return;\r
2051   if (recurse > 0) return;\r
2052   recurse++;\r
2053   while (newSize > 0 &&\r
2054          (newSizeX < sizeInfo[newSize].cliWidth ||\r
2055           newSizeY < sizeInfo[newSize].cliHeight)) {\r
2056     newSize--;\r
2057   } \r
2058   boardSize = newSize;\r
2059   InitDrawingSizes(boardSize, flags);\r
2060   recurse--;\r
2061 }\r
2062 \r
2063 \r
2064 \r
2065 VOID\r
2066 InitDrawingSizes(BoardSize boardSize, int flags)\r
2067 {\r
2068   int i, boardWidth;\r
2069   ChessSquare piece;\r
2070   static int oldBoardSize = -1, oldTinyLayout = 0;\r
2071   HDC hdc;\r
2072   SIZE clockSize, messageSize;\r
2073   HFONT oldFont;\r
2074   char buf[MSG_SIZ];\r
2075   char *str;\r
2076   HMENU hmenu = GetMenu(hwndMain);\r
2077   RECT crect, wrect;\r
2078   int offby;\r
2079   LOGBRUSH logbrush;\r
2080 \r
2081   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
2082   smallLayout = sizeInfo[boardSize].smallLayout;\r
2083   squareSize = sizeInfo[boardSize].squareSize;\r
2084   lineGap = sizeInfo[boardSize].lineGap;\r
2085 \r
2086   if (tinyLayout != oldTinyLayout) {\r
2087     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
2088     if (tinyLayout) {\r
2089       style &= ~WS_SYSMENU;\r
2090       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
2091                  "&Minimize\tCtrl+F4");\r
2092     } else {\r
2093       style |= WS_SYSMENU;\r
2094       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
2095     }\r
2096     SetWindowLong(hwndMain, GWL_STYLE, style);\r
2097 \r
2098     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
2099       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
2100         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
2101     }\r
2102     DrawMenuBar(hwndMain);\r
2103   }\r
2104 \r
2105   boardWidth = BoardWidth(boardSize);\r
2106 \r
2107   /* Get text area sizes */\r
2108   hdc = GetDC(hwndMain);\r
2109   if (appData.clockMode) {\r
2110     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
2111   } else {\r
2112     sprintf(buf, "White");\r
2113   }\r
2114   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
2115   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
2116   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2117   str = "We only care about the height here";\r
2118   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
2119   SelectObject(hdc, oldFont);\r
2120   ReleaseDC(hwndMain, hdc);\r
2121 \r
2122   /* Compute where everything goes */\r
2123   whiteRect.left = OUTER_MARGIN;\r
2124   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
2125   whiteRect.top = OUTER_MARGIN;\r
2126   whiteRect.bottom = whiteRect.top + clockSize.cy;\r
2127 \r
2128   blackRect.left = whiteRect.right + INNER_MARGIN;\r
2129   blackRect.right = blackRect.left + boardWidth/2 - 1;\r
2130   blackRect.top = whiteRect.top;\r
2131   blackRect.bottom = whiteRect.bottom;\r
2132 \r
2133   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;\r
2134   if (appData.showButtonBar) {\r
2135     messageRect.right = blackRect.right\r
2136       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
2137   } else {\r
2138     messageRect.right = blackRect.right;\r
2139   }\r
2140   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
2141   messageRect.bottom = messageRect.top + messageSize.cy;\r
2142 \r
2143   boardRect.left = whiteRect.left;\r
2144   boardRect.right = boardRect.left + boardWidth;\r
2145   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
2146   boardRect.bottom = boardRect.top + boardWidth;\r
2147 \r
2148   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
2149   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
2150   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
2151   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
2152     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
2153   GetWindowRect(hwndMain, &wrect);\r
2154   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2155                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2156   /* compensate if menu bar wrapped */\r
2157   GetClientRect(hwndMain, &crect);\r
2158   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
2159   winHeight += offby;\r
2160   switch (flags) {\r
2161   case WMSZ_TOPLEFT:\r
2162     SetWindowPos(hwndMain, NULL, \r
2163                  wrect.right - winWidth, wrect.bottom - winHeight, \r
2164                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2165     break;\r
2166 \r
2167   case WMSZ_TOPRIGHT:\r
2168   case WMSZ_TOP:\r
2169     SetWindowPos(hwndMain, NULL, \r
2170                  wrect.left, wrect.bottom - winHeight, \r
2171                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2172     break;\r
2173 \r
2174   case WMSZ_BOTTOMLEFT:\r
2175   case WMSZ_LEFT:\r
2176     SetWindowPos(hwndMain, NULL, \r
2177                  wrect.right - winWidth, wrect.top, \r
2178                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
2179     break;\r
2180 \r
2181   case WMSZ_BOTTOMRIGHT:\r
2182   case WMSZ_BOTTOM:\r
2183   case WMSZ_RIGHT:\r
2184   default:\r
2185     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
2186                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
2187     break;\r
2188   }\r
2189 \r
2190   hwndPause = NULL;\r
2191   for (i = 0; i < N_BUTTONS; i++) {\r
2192     if (buttonDesc[i].hwnd != NULL) {\r
2193       DestroyWindow(buttonDesc[i].hwnd);\r
2194       buttonDesc[i].hwnd = NULL;\r
2195     }\r
2196     if (appData.showButtonBar) {\r
2197       buttonDesc[i].hwnd =\r
2198         CreateWindow("BUTTON", buttonDesc[i].label,\r
2199                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
2200                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
2201                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
2202                      (HMENU) buttonDesc[i].id,\r
2203                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
2204       if (tinyLayout) {\r
2205         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
2206                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
2207                     MAKELPARAM(FALSE, 0));\r
2208       }\r
2209       if (buttonDesc[i].id == IDM_Pause)\r
2210         hwndPause = buttonDesc[i].hwnd;\r
2211       buttonDesc[i].wndproc = (WNDPROC)\r
2212         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
2213     }\r
2214   }\r
2215   if (gridPen != NULL) DeleteObject(gridPen);\r
2216   if (highlightPen != NULL) DeleteObject(highlightPen);\r
2217   if (premovePen != NULL) DeleteObject(premovePen);\r
2218   if (lineGap != 0) {\r
2219     logbrush.lbStyle = BS_SOLID;\r
2220     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
2221     gridPen =\r
2222       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2223                    lineGap, &logbrush, 0, NULL);\r
2224     logbrush.lbColor = highlightSquareColor;\r
2225     highlightPen =\r
2226       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2227                    lineGap, &logbrush, 0, NULL);\r
2228 \r
2229     logbrush.lbColor = premoveHighlightColor; \r
2230     premovePen =\r
2231       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
2232                    lineGap, &logbrush, 0, NULL);\r
2233 \r
2234     for (i = 0; i < BOARD_SIZE + 1; i++) {\r
2235       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
2236       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;\r
2237       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
2238         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
2239       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
2240         BOARD_SIZE * (squareSize + lineGap);\r
2241       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =\r
2242         gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +\r
2243         lineGap / 2 + (i * (squareSize + lineGap));\r
2244       gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =\r
2245         boardRect.top + BOARD_SIZE * (squareSize + lineGap);\r
2246       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
2247     }\r
2248   }\r
2249 \r
2250   if (boardSize == oldBoardSize) return;\r
2251   oldBoardSize = boardSize;\r
2252   oldTinyLayout = tinyLayout;\r
2253 \r
2254   /* Load piece bitmaps for this board size */\r
2255   for (i=0; i<=2; i++) {\r
2256     for (piece = WhitePawn;\r
2257          (int) piece <= (int) WhiteKing;\r
2258          piece = (ChessSquare) ((int) piece + 1)) {\r
2259       if (pieceBitmap[i][piece] != NULL)\r
2260         DeleteObject(pieceBitmap[i][piece]);\r
2261     }\r
2262   }\r
2263 \r
2264   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
2265   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
2266   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
2267   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
2268   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
2269   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
2270   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
2271   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
2272   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
2273   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
2274   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
2275   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
2276   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
2277   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
2278   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
2279   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
2280   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
2281   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
2282 \r
2283 }\r
2284 \r
2285 HBITMAP\r
2286 PieceBitmap(ChessSquare p, int kind)\r
2287 {\r
2288   if ((int) p >= (int) BlackPawn)\r
2289     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
2290 \r
2291   return pieceBitmap[kind][(int) p];\r
2292 }\r
2293 \r
2294 /***************************************************************/\r
2295 \r
2296 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
2297 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
2298 /*\r
2299 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
2300 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
2301 */\r
2302 \r
2303 VOID\r
2304 SquareToPos(int row, int column, int * x, int * y)\r
2305 {\r
2306   if (flipView) {\r
2307     *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
2308     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
2309   } else {\r
2310     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
2311     *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
2312   }\r
2313 }\r
2314 \r
2315 VOID\r
2316 DrawCoordsOnDC(HDC hdc)\r
2317 {\r
2318   static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};\r
2319   static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};\r
2320   char str[2] = { NULLCHAR, NULLCHAR };\r
2321   int oldMode, oldAlign, x, y, start, i;\r
2322   HFONT oldFont;\r
2323   HBRUSH oldBrush;\r
2324 \r
2325   if (!appData.showCoords)\r
2326     return;\r
2327 \r
2328   start = flipView ? 0 : 8;\r
2329 \r
2330   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
2331   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
2332   oldAlign = GetTextAlign(hdc);\r
2333   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
2334 \r
2335   y = boardRect.top + lineGap;\r
2336   x = boardRect.left + lineGap;\r
2337 \r
2338   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
2339   for (i = 0; i < 8; i++) {\r
2340     str[0] = files[start + i];\r
2341     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
2342     y += squareSize + lineGap;\r
2343   }\r
2344 \r
2345   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
2346   for (i = 0; i < 8; i++) {\r
2347     str[0] = ranks[start + i];\r
2348     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
2349     x += squareSize + lineGap;\r
2350   }    \r
2351 \r
2352   SelectObject(hdc, oldBrush);\r
2353   SetBkMode(hdc, oldMode);\r
2354   SetTextAlign(hdc, oldAlign);\r
2355   SelectObject(hdc, oldFont);\r
2356 }\r
2357 \r
2358 VOID\r
2359 DrawGridOnDC(HDC hdc)\r
2360 {\r
2361   HPEN oldPen;\r
2362  \r
2363   if (lineGap != 0) {\r
2364     oldPen = SelectObject(hdc, gridPen);\r
2365     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);\r
2366     SelectObject(hdc, oldPen);\r
2367   }\r
2368 }\r
2369 \r
2370 #define HIGHLIGHT_PEN 0\r
2371 #define PREMOVE_PEN   1\r
2372 \r
2373 VOID\r
2374 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
2375 {\r
2376   int x1, y1;\r
2377   HPEN oldPen, hPen;\r
2378   if (lineGap == 0) return;\r
2379   if (flipView) {\r
2380     x1 = boardRect.left +\r
2381       lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);\r
2382     y1 = boardRect.top +\r
2383       lineGap/2 + y * (squareSize + lineGap);\r
2384   } else {\r
2385     x1 = boardRect.left +\r
2386       lineGap/2 + x * (squareSize + lineGap);\r
2387     y1 = boardRect.top +\r
2388       lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);\r
2389   }\r
2390   hPen = pen ? premovePen : highlightPen;\r
2391   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
2392   MoveToEx(hdc, x1, y1, NULL);\r
2393   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
2394   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
2395   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
2396   LineTo(hdc, x1, y1);\r
2397   SelectObject(hdc, oldPen);\r
2398 }\r
2399 \r
2400 VOID\r
2401 DrawHighlightsOnDC(HDC hdc)\r
2402 {\r
2403   int i;\r
2404   for (i=0; i<2; i++) {\r
2405     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
2406       DrawHighlightOnDC(hdc, TRUE,\r
2407                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
2408                         HIGHLIGHT_PEN);\r
2409   }\r
2410   for (i=0; i<2; i++) {\r
2411     if (premoveHighlightInfo.sq[i].x >= 0 && \r
2412         premoveHighlightInfo.sq[i].y >= 0) {\r
2413         DrawHighlightOnDC(hdc, TRUE,\r
2414                           premoveHighlightInfo.sq[i].x, \r
2415                           premoveHighlightInfo.sq[i].y,\r
2416                           PREMOVE_PEN);\r
2417     }\r
2418   }\r
2419 }\r
2420 \r
2421 /* Note: sqcolor is used only in monoMode */\r
2422 /* Note that this code is largely duplicated in woptions.c,\r
2423    function DrawSampleSquare, so that needs to be updated too */\r
2424 VOID\r
2425 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
2426 {\r
2427   HBITMAP oldBitmap;\r
2428   HBRUSH oldBrush;\r
2429 \r
2430   if (appData.blindfold) return;\r
2431 \r
2432   if (appData.monoMode) {\r
2433     SelectObject(tmphdc, PieceBitmap(piece, \r
2434       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
2435     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
2436            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
2437   } else {\r
2438     if (color) {\r
2439       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
2440       oldBrush = SelectObject(hdc, whitePieceBrush);\r
2441       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2442 #if 0\r
2443       /* Use black piece color for outline of white pieces */\r
2444       /* Not sure this looks really good (though xboard does it).\r
2445          Maybe better to have another selectable color, default black */\r
2446       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
2447       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2448       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2449 #else\r
2450       /* Use black for outline of white pieces */\r
2451       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
2452       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);\r
2453 #endif\r
2454     } else {\r
2455 #if 0\r
2456       /* Use white piece color for details of black pieces */\r
2457       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
2458          WHITE_PIECE ones aren't always the right shape. */\r
2459       /* Not sure this looks really good (though xboard does it).\r
2460          Maybe better to have another selectable color, default medium gray? */\r
2461       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
2462       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
2463       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2464       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2465       SelectObject(hdc, blackPieceBrush);\r
2466       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2467 #else\r
2468       /* Use square color for details of black pieces */\r
2469       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
2470       oldBrush = SelectObject(hdc, blackPieceBrush);\r
2471       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);\r
2472 #endif\r
2473     }\r
2474     SelectObject(hdc, oldBrush);\r
2475     SelectObject(tmphdc, oldBitmap);\r
2476   }\r
2477 }\r
2478 \r
2479 VOID\r
2480 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
2481 {\r
2482   int row, column, x, y, square_color, piece_color;\r
2483   ChessSquare piece;\r
2484   HBRUSH oldBrush;\r
2485 \r
2486   for (row = 0; row < BOARD_SIZE; row++) {\r
2487     for (column = 0; column < BOARD_SIZE; column++) {\r
2488   \r
2489       SquareToPos(row, column, &x, &y);\r
2490 \r
2491       piece = board[row][column];\r
2492 \r
2493       square_color = ((column + row) % 2) == 1;\r
2494       piece_color = (int) piece < (int) BlackPawn;\r
2495 \r
2496       if (appData.monoMode) {\r
2497         if (piece == EmptySquare) {\r
2498           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
2499                  square_color ? WHITENESS : BLACKNESS);\r
2500         } else {\r
2501           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
2502         }\r
2503       } else {\r
2504         oldBrush = SelectObject(hdc, square_color ?\r
2505                                 lightSquareBrush : darkSquareBrush);\r
2506         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
2507         SelectObject(hdc, oldBrush);\r
2508         if (piece != EmptySquare)\r
2509           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
2510       }\r
2511     }\r
2512   }\r
2513 }\r
2514 \r
2515 #define MAX_CLIPS 200   /* more than enough */\r
2516 \r
2517 VOID\r
2518 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
2519 {\r
2520   static Board lastReq, lastDrawn;\r
2521   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
2522   static int lastDrawnFlipView = 0;\r
2523   static int lastReqValid = 0, lastDrawnValid = 0;\r
2524   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
2525   HDC tmphdc;\r
2526   HDC hdcmem;\r
2527   HBITMAP bufferBitmap;\r
2528   HBITMAP oldBitmap;\r
2529   RECT Rect;\r
2530   HRGN clips[MAX_CLIPS];\r
2531   ChessSquare dragged_piece = EmptySquare;\r
2532 \r
2533   /* I'm undecided on this - this function figures out whether a full\r
2534    * repaint is necessary on its own, so there's no real reason to have the\r
2535    * caller tell it that.  I think this can safely be set to FALSE - but\r
2536    * if we trust the callers not to request full repaints unnessesarily, then\r
2537    * we could skip some clipping work.  In other words, only request a full\r
2538    * redraw when the majority of pieces have changed positions (ie. flip, \r
2539    * gamestart and similar)  --Hawk\r
2540    */\r
2541   Boolean fullrepaint = repaint;\r
2542 \r
2543   if (board == NULL) {\r
2544     if (!lastReqValid) {\r
2545       return;\r
2546     }\r
2547     board = lastReq;\r
2548   } else {\r
2549     CopyBoard(lastReq, board);\r
2550     lastReqValid = 1;\r
2551   }\r
2552 \r
2553   if (doingSizing) {\r
2554     return;\r
2555   }\r
2556 \r
2557   if (IsIconic(hwndMain)) {\r
2558     return;\r
2559   }\r
2560 \r
2561   if (hdc == NULL) {\r
2562     hdc = GetDC(hwndMain);\r
2563     if (!appData.monoMode) {\r
2564       SelectPalette(hdc, hPal, FALSE);\r
2565       RealizePalette(hdc);\r
2566     }\r
2567     releaseDC = TRUE;\r
2568   } else {\r
2569     releaseDC = FALSE;\r
2570   }\r
2571 \r
2572 #if 0\r
2573   fprintf(debugFP, "*******************************\n"\r
2574                    "repaint = %s\n"\r
2575                    "dragInfo.from (%d,%d)\n"\r
2576                    "dragInfo.start (%d,%d)\n"\r
2577                    "dragInfo.pos (%d,%d)\n"\r
2578                    "dragInfo.lastpos (%d,%d)\n", \r
2579                     repaint ? "TRUE" : "FALSE",\r
2580                     dragInfo.from.x, dragInfo.from.y, \r
2581                     dragInfo.start.x, dragInfo.start.y,\r
2582                     dragInfo.pos.x, dragInfo.pos.y,\r
2583                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
2584   fprintf(debugFP, "prev:  ");\r
2585   for (row = 0; row < 8; row++) {\r
2586     for (column = 0; column < 8; column++) {\r
2587       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
2588     }\r
2589   }\r
2590   fprintf(debugFP, "\n");\r
2591   fprintf(debugFP, "board: ");\r
2592   for (row = 0; row < 8; row++) {\r
2593     for (column = 0; column < 8; column++) {\r
2594       fprintf(debugFP, "%d ", board[row][column]);\r
2595     }\r
2596   }\r
2597   fprintf(debugFP, "\n");\r
2598   fflush(debugFP);\r
2599 #endif\r
2600 \r
2601   /* Create some work-DCs */\r
2602   hdcmem = CreateCompatibleDC(hdc);\r
2603   tmphdc = CreateCompatibleDC(hdc);\r
2604 \r
2605   /* Figure out which squares need updating by comparing the \r
2606    * newest board with the last drawn board and checking if\r
2607    * flipping has changed.\r
2608    */\r
2609   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
2610     for (row = 0; row < 8; row++) {\r
2611       for (column = 0; column < 8; column++) {\r
2612         if (lastDrawn[row][column] != board[row][column]) {\r
2613           SquareToPos(row, column, &x, &y);\r
2614           clips[num_clips++] =\r
2615             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
2616         }\r
2617       }\r
2618     }\r
2619     for (i=0; i<2; i++) {\r
2620       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
2621           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
2622         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
2623             lastDrawnHighlight.sq[i].y >= 0) {\r
2624           SquareToPos(lastDrawnHighlight.sq[i].y,\r
2625                       lastDrawnHighlight.sq[i].x, &x, &y);\r
2626           clips[num_clips++] =\r
2627             CreateRectRgn(x - lineGap, y - lineGap, \r
2628                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2629         }\r
2630         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
2631           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
2632           clips[num_clips++] =\r
2633             CreateRectRgn(x - lineGap, y - lineGap, \r
2634                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2635         }\r
2636       }\r
2637     }\r
2638     for (i=0; i<2; i++) {\r
2639       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
2640           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
2641         if (lastDrawnPremove.sq[i].x >= 0 &&\r
2642             lastDrawnPremove.sq[i].y >= 0) {\r
2643           SquareToPos(lastDrawnPremove.sq[i].y,\r
2644                       lastDrawnPremove.sq[i].x, &x, &y);\r
2645           clips[num_clips++] =\r
2646             CreateRectRgn(x - lineGap, y - lineGap, \r
2647                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2648         }\r
2649         if (premoveHighlightInfo.sq[i].x >= 0 && \r
2650             premoveHighlightInfo.sq[i].y >= 0) {\r
2651           SquareToPos(premoveHighlightInfo.sq[i].y, \r
2652                       premoveHighlightInfo.sq[i].x, &x, &y);\r
2653           clips[num_clips++] =\r
2654             CreateRectRgn(x - lineGap, y - lineGap, \r
2655                           x + squareSize + lineGap, y + squareSize + lineGap);\r
2656         }\r
2657       }\r
2658     }\r
2659   } else {\r
2660     fullrepaint = TRUE;\r
2661   }\r
2662 \r
2663   /* Create a buffer bitmap - this is the actual bitmap\r
2664    * being written to.  When all the work is done, we can\r
2665    * copy it to the real DC (the screen).  This avoids\r
2666    * the problems with flickering.\r
2667    */\r
2668   GetClientRect(hwndMain, &Rect);\r
2669   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
2670                                         Rect.bottom-Rect.top+1);\r
2671   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
2672   if (!appData.monoMode) {\r
2673     SelectPalette(hdcmem, hPal, FALSE);\r
2674   }\r
2675 \r
2676   /* Create clips for dragging */\r
2677   if (!fullrepaint) {\r
2678     if (dragInfo.from.x >= 0) {\r
2679       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
2680       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2681     }\r
2682     if (dragInfo.start.x >= 0) {\r
2683       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
2684       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2685     }\r
2686     if (dragInfo.pos.x >= 0) {\r
2687       x = dragInfo.pos.x - squareSize / 2;\r
2688       y = dragInfo.pos.y - squareSize / 2;\r
2689       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2690     }\r
2691     if (dragInfo.lastpos.x >= 0) {\r
2692       x = dragInfo.lastpos.x - squareSize / 2;\r
2693       y = dragInfo.lastpos.y - squareSize / 2;\r
2694       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
2695     }\r
2696   }\r
2697 \r
2698   /* If dragging is in progress, we temporarely remove the piece */\r
2699   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
2700     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
2701     board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
2702   }\r
2703 \r
2704   /* Are we animating a move?  \r
2705    * If so, \r
2706    *   - remove the piece from the board (temporarely)\r
2707    *   - calculate the clipping region\r
2708    */\r
2709   if (!fullrepaint) {\r
2710     if (animInfo.piece != EmptySquare) {\r
2711       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
2712       x = boardRect.left + animInfo.lastpos.x;\r
2713       y = boardRect.top + animInfo.lastpos.y;\r
2714       x2 = boardRect.left + animInfo.pos.x;\r
2715       y2 = boardRect.top + animInfo.pos.y;\r
2716       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
2717       /* Slight kludge.  The real problem is that after AnimateMove is\r
2718          done, the position on the screen does not match lastDrawn.\r
2719          This currently causes trouble only on e.p. captures in\r
2720          atomic, where the piece moves to an empty square and then\r
2721          explodes.  The old and new positions both had an empty square\r
2722          at the destination, but animation has drawn a piece there and\r
2723          we have to remember to erase it. */\r
2724       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
2725     }\r
2726   }\r
2727 \r
2728   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
2729   if (num_clips == 0)\r
2730     fullrepaint = TRUE;\r
2731 \r
2732   /* Set clipping on the memory DC */\r
2733   if (!fullrepaint) {\r
2734     SelectClipRgn(hdcmem, clips[0]);\r
2735     for (x = 1; x < num_clips; x++) {\r
2736       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
2737         abort();  // this should never ever happen!\r
2738     }\r
2739   }\r
2740 \r
2741   /* Do all the drawing to the memory DC */\r
2742   DrawGridOnDC(hdcmem);\r
2743   DrawHighlightsOnDC(hdcmem);\r
2744   DrawBoardOnDC(hdcmem, board, tmphdc);\r
2745   DrawCoordsOnDC(hdcmem);\r
2746 \r
2747   /* Put the dragged piece back into place and draw it */\r
2748   if (dragged_piece != EmptySquare) {\r
2749     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
2750     x = dragInfo.pos.x - squareSize / 2;\r
2751     y = dragInfo.pos.y - squareSize / 2;\r
2752     DrawPieceOnDC(hdcmem, dragged_piece,\r
2753                   ((int) dragged_piece < (int) BlackPawn), \r
2754                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
2755   }   \r
2756   \r
2757   /* Put the animated piece back into place and draw it */\r
2758   if (animInfo.piece != EmptySquare) {\r
2759     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
2760     x = boardRect.left + animInfo.pos.x;\r
2761     y = boardRect.top + animInfo.pos.y;\r
2762     DrawPieceOnDC(hdcmem, animInfo.piece,\r
2763                   ((int) animInfo.piece < (int) BlackPawn),\r
2764                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
2765   }\r
2766 \r
2767   /* Release the bufferBitmap by selecting in the old bitmap \r
2768    * and delete the memory DC\r
2769    */\r
2770   SelectObject(hdcmem, oldBitmap);\r
2771   DeleteDC(hdcmem);\r
2772 \r
2773   /* Set clipping on the target DC */\r
2774   if (!fullrepaint) {\r
2775     SelectClipRgn(hdc, clips[0]);\r
2776     for (x = 1; x < num_clips; x++) {\r
2777       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
2778         abort();   // this should never ever happen!\r
2779     } \r
2780   }\r
2781 \r
2782   /* Copy the new bitmap onto the screen in one go.\r
2783    * This way we avoid any flickering\r
2784    */\r
2785   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
2786   BitBlt(hdc, boardRect.left, boardRect.top,\r
2787          boardRect.right - boardRect.left,\r
2788          boardRect.bottom - boardRect.top,\r
2789          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
2790   SelectObject(tmphdc, oldBitmap);\r
2791 \r
2792   /* Massive cleanup */\r
2793   for (x = 0; x < num_clips; x++)\r
2794     DeleteObject(clips[x]);\r
2795 \r
2796   DeleteDC(tmphdc);\r
2797   DeleteObject(bufferBitmap);\r
2798 \r
2799   if (releaseDC) \r
2800     ReleaseDC(hwndMain, hdc);\r
2801   \r
2802   if (lastDrawnFlipView != flipView) {\r
2803     if (flipView)\r
2804       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
2805     else\r
2806       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
2807   }\r
2808 \r
2809   CopyBoard(lastDrawn, board);\r
2810   lastDrawnHighlight = highlightInfo;\r
2811   lastDrawnPremove   = premoveHighlightInfo;\r
2812   lastDrawnFlipView = flipView;\r
2813   lastDrawnValid = 1;\r
2814 }\r
2815 \r
2816 \r
2817 /*---------------------------------------------------------------------------*\\r
2818 | CLIENT PAINT PROCEDURE\r
2819 |   This is the main event-handler for the WM_PAINT message.\r
2820 |\r
2821 \*---------------------------------------------------------------------------*/\r
2822 VOID\r
2823 PaintProc(HWND hwnd)\r
2824 {\r
2825   HDC         hdc;\r
2826   PAINTSTRUCT ps;\r
2827   HFONT       oldFont;\r
2828 \r
2829   if(hdc = BeginPaint(hwnd, &ps)) {\r
2830     if (IsIconic(hwnd)) {\r
2831       DrawIcon(hdc, 2, 2, iconCurrent);\r
2832     } else {\r
2833       if (!appData.monoMode) {\r
2834         SelectPalette(hdc, hPal, FALSE);\r
2835         RealizePalette(hdc);\r
2836       }\r
2837       HDCDrawPosition(hdc, 1, NULL);\r
2838       oldFont =\r
2839         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
2840       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
2841                  ETO_CLIPPED|ETO_OPAQUE,\r
2842                  &messageRect, messageText, strlen(messageText), NULL);\r
2843       SelectObject(hdc, oldFont);\r
2844       DisplayBothClocks();\r
2845     }\r
2846     EndPaint(hwnd,&ps);\r
2847   }\r
2848 \r
2849   return;\r
2850 }\r
2851 \r
2852 \r
2853 /*\r
2854  * If the user selects on a border boundary, return -1; if off the board,\r
2855  *   return -2.  Otherwise map the event coordinate to the square.\r
2856  * The offset boardRect.left or boardRect.top must already have been\r
2857  *   subtracted from x.\r
2858  */\r
2859 int\r
2860 EventToSquare(int x)\r
2861 {\r
2862   if (x <= 0)\r
2863     return -2;\r
2864   if (x < lineGap)\r
2865     return -1;\r
2866   x -= lineGap;\r
2867   if ((x % (squareSize + lineGap)) >= squareSize)\r
2868     return -1;\r
2869   x /= (squareSize + lineGap);\r
2870   if (x >= BOARD_SIZE)\r
2871     return -2;\r
2872   return x;\r
2873 }\r
2874 \r
2875 typedef struct {\r
2876   char piece;\r
2877   int command;\r
2878   char* name;\r
2879 } DropEnable;\r
2880 \r
2881 DropEnable dropEnables[] = {\r
2882   { 'P', DP_Pawn, "Pawn" },\r
2883   { 'N', DP_Knight, "Knight" },\r
2884   { 'B', DP_Bishop, "Bishop" },\r
2885   { 'R', DP_Rook, "Rook" },\r
2886   { 'Q', DP_Queen, "Queen" },\r
2887 };\r
2888 \r
2889 VOID\r
2890 SetupDropMenu(HMENU hmenu)\r
2891 {\r
2892   int i, count, enable;\r
2893   char *p;\r
2894   extern char white_holding[], black_holding[];\r
2895   char item[MSG_SIZ];\r
2896 \r
2897   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
2898     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
2899                dropEnables[i].piece);\r
2900     count = 0;\r
2901     while (p && *p++ == dropEnables[i].piece) count++;\r
2902     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
2903     enable = count > 0 || !appData.testLegality\r
2904       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
2905                       && !appData.icsActive);\r
2906     ModifyMenu(hmenu, dropEnables[i].command,\r
2907                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
2908                dropEnables[i].command, item);\r
2909   }\r
2910 }\r
2911 \r
2912 static int fromX = -1, fromY = -1, toX, toY;\r
2913 \r
2914 /* Event handler for mouse messages */\r
2915 VOID\r
2916 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
2917 {\r
2918   int x, y;\r
2919   POINT pt;\r
2920   static int recursive = 0;\r
2921   HMENU hmenu;\r
2922   BOOLEAN saveAnimate;\r
2923   static BOOLEAN sameAgain = FALSE;\r
2924 \r
2925   if (recursive) {\r
2926     if (message == WM_MBUTTONUP) {\r
2927       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
2928          to the middle button: we simulate pressing the left button too!\r
2929          */\r
2930       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
2931       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
2932     }\r
2933     return;\r
2934   }\r
2935   recursive++;\r
2936   \r
2937   pt.x = LOWORD(lParam);\r
2938   pt.y = HIWORD(lParam);\r
2939   x = EventToSquare(pt.x - boardRect.left);\r
2940   y = EventToSquare(pt.y - boardRect.top);\r
2941   if (!flipView && y >= 0) {\r
2942     y = BOARD_SIZE - 1 - y;\r
2943   }\r
2944   if (flipView && x >= 0) {\r
2945     x = BOARD_SIZE - 1 - x;\r
2946   }\r
2947 \r
2948   switch (message) {\r
2949   case WM_LBUTTONDOWN:\r
2950     ErrorPopDown();\r
2951     sameAgain = FALSE;\r
2952     if (y == -2) {\r
2953       /* Downclick vertically off board; check if on clock */\r
2954       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
2955         if (gameMode == EditPosition) {\r
2956           SetWhiteToPlayEvent();\r
2957         } else if (gameMode == IcsPlayingBlack ||\r
2958                    gameMode == MachinePlaysWhite) {\r
2959           CallFlagEvent();\r
2960         }\r
2961       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
2962         if (gameMode == EditPosition) {\r
2963           SetBlackToPlayEvent();\r
2964         } else if (gameMode == IcsPlayingWhite ||\r
2965                    gameMode == MachinePlaysBlack) {\r
2966           CallFlagEvent();\r
2967         }\r
2968       }\r
2969       if (!appData.highlightLastMove) {\r
2970         ClearHighlights();\r
2971         DrawPosition(FALSE, NULL);\r
2972       }\r
2973       fromX = fromY = -1;\r
2974       dragInfo.start.x = dragInfo.start.y = -1;\r
2975       dragInfo.from = dragInfo.start;\r
2976       break;\r
2977     } else if (x < 0 || y < 0) {\r
2978       break;\r
2979     } else if (fromX == x && fromY == y) {\r
2980       /* Downclick on same square again */\r
2981       ClearHighlights();\r
2982       DrawPosition(FALSE, NULL);\r
2983       sameAgain = TRUE;  \r
2984     } else if (fromX != -1) {\r
2985       /* Downclick on different square */\r
2986       ChessSquare pdown, pup;\r
2987       pdown = boards[currentMove][fromY][fromX];\r
2988       pup = boards[currentMove][y][x];\r
2989       if (gameMode == EditPosition ||\r
2990           !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
2991              WhitePawn <= pup && pup <= WhiteKing) ||\r
2992             (BlackPawn <= pdown && pdown <= BlackKing &&\r
2993              BlackPawn <= pup && pup <= BlackKing))) {\r
2994         /* EditPosition, empty square, or different color piece;\r
2995            click-click move is possible */\r
2996         toX = x;\r
2997         toY = y;\r
2998         if (IsPromotion(fromX, fromY, toX, toY)) {\r
2999           if (appData.alwaysPromoteToQueen) {\r
3000             UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
3001             if (!appData.highlightLastMove) {\r
3002               ClearHighlights();\r
3003               DrawPosition(FALSE, NULL);\r
3004             }\r
3005           } else {\r
3006             SetHighlights(fromX, fromY, toX, toY);\r
3007             DrawPosition(FALSE, NULL);\r
3008             PromotionPopup(hwnd);\r
3009           }\r
3010         } else {        /* not a promotion */\r
3011           if (appData.animate || appData.highlightLastMove) {\r
3012             SetHighlights(fromX, fromY, toX, toY);\r
3013           } else {\r
3014             ClearHighlights();\r
3015           }\r
3016           UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
3017           if (appData.animate && !appData.highlightLastMove) {\r
3018             ClearHighlights();\r
3019             DrawPosition(FALSE, NULL);\r
3020           }\r
3021         }\r
3022         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
3023         fromX = fromY = -1;\r
3024         break;\r
3025       }\r
3026       ClearHighlights();\r
3027       DrawPosition(FALSE, NULL);\r
3028     }\r
3029     /* First downclick, or restart on a square with same color piece */\r
3030     if (!frozen && OKToStartUserMove(x, y)) {\r
3031       fromX = x;\r
3032       fromY = y;\r
3033       dragInfo.lastpos = pt;\r
3034       dragInfo.from.x = fromX;\r
3035       dragInfo.from.y = fromY;\r
3036       dragInfo.start = dragInfo.from;\r
3037       SetCapture(hwndMain);\r
3038     } else {\r
3039       fromX = fromY = -1;\r
3040       dragInfo.start.x = dragInfo.start.y = -1;\r
3041       dragInfo.from = dragInfo.start;\r
3042     }\r
3043     break;\r
3044 \r
3045   case WM_LBUTTONUP:\r
3046     ReleaseCapture();\r
3047     if (fromX == -1) break;\r
3048     if (x == fromX && y == fromY) {\r
3049       dragInfo.from.x = dragInfo.from.y = -1;\r
3050       /* Upclick on same square */\r
3051       if (sameAgain) {\r
3052         /* Clicked same square twice: abort click-click move */\r
3053         fromX = fromY = -1;\r
3054         gotPremove = 0;\r
3055         ClearPremoveHighlights();\r
3056       } else {\r
3057         /* First square clicked: start click-click move */\r
3058         SetHighlights(fromX, fromY, -1, -1);\r
3059       }\r
3060       DrawPosition(FALSE, NULL);\r
3061     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
3062       /* Errant click; ignore */\r
3063       break;\r
3064     } else {\r
3065       /* Finish drag move */\r
3066       dragInfo.from.x = dragInfo.from.y = -1;\r
3067       toX = x;\r
3068       toY = y;\r
3069       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
3070       appData.animate = appData.animate && !appData.animateDragging;\r
3071       if (IsPromotion(fromX, fromY, toX, toY)) {\r
3072         if (appData.alwaysPromoteToQueen) {\r
3073           UserMoveEvent(fromX, fromY, toX, toY, 'q');\r
3074         } else {\r
3075           DrawPosition(FALSE, NULL);\r
3076           PromotionPopup(hwnd);\r
3077         }\r
3078       } else {\r
3079         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);\r
3080       }\r
3081       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
3082       appData.animate = saveAnimate;\r
3083       fromX = fromY = -1;\r
3084       if (appData.highlightDragging && !appData.highlightLastMove) {\r
3085         ClearHighlights();\r
3086       }\r
3087       if (appData.animate || appData.animateDragging ||\r
3088           appData.highlightDragging || gotPremove) {\r
3089         DrawPosition(FALSE, NULL);\r
3090       }\r
3091     }\r
3092     dragInfo.start.x = dragInfo.start.y = -1; \r
3093     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
3094     break;\r
3095 \r
3096   case WM_MOUSEMOVE:\r
3097     if ((appData.animateDragging || appData.highlightDragging)\r
3098         && (wParam & MK_LBUTTON)\r
3099         && dragInfo.from.x >= 0) {\r
3100       if (appData.animateDragging) {\r
3101         dragInfo.pos = pt;\r
3102       }\r
3103       if (appData.highlightDragging) {\r
3104         SetHighlights(fromX, fromY, x, y);\r
3105       }\r
3106       DrawPosition(FALSE, NULL);\r
3107       dragInfo.lastpos = dragInfo.pos;\r
3108     }\r
3109     break;\r
3110 \r
3111   case WM_MBUTTONDOWN:\r
3112   case WM_RBUTTONDOWN:\r
3113     ErrorPopDown();\r
3114     ReleaseCapture();\r
3115     fromX = fromY = -1;\r
3116     dragInfo.pos.x = dragInfo.pos.y = -1;\r
3117     dragInfo.start.x = dragInfo.start.y = -1;\r
3118     dragInfo.from = dragInfo.start;\r
3119     dragInfo.lastpos = dragInfo.pos;\r
3120     if (appData.highlightDragging) {\r
3121       ClearHighlights();\r
3122     }\r
3123     DrawPosition(TRUE, NULL);\r
3124 \r
3125     switch (gameMode) {\r
3126     case EditPosition:\r
3127     case IcsExamining:\r
3128       if (x < 0 || y < 0) break;\r
3129       fromX = x;\r
3130       fromY = y;\r
3131       if (message == WM_MBUTTONDOWN) {\r
3132         buttonCount = 3;  /* even if system didn't think so */\r
3133         if (wParam & MK_SHIFT) \r
3134           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3135         else\r
3136           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3137       } else { /* message == WM_RBUTTONDOWN */\r
3138 #if 0\r
3139         if (buttonCount == 3) {\r
3140           if (wParam & MK_SHIFT) \r
3141             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
3142           else\r
3143             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
3144         } else {\r
3145           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3146         }\r
3147 #else\r
3148         /* Just have one menu, on the right button.  Windows users don't\r
3149            think to try the middle one, and sometimes other software steals\r
3150            it, or it doesn't really exist. */\r
3151         MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
3152 #endif\r
3153       }\r
3154       break;\r
3155     case IcsPlayingWhite:\r
3156     case IcsPlayingBlack:\r
3157     case EditGame:\r
3158     case MachinePlaysWhite:\r
3159     case MachinePlaysBlack:\r
3160       if (appData.testLegality &&\r
3161           gameInfo.variant != VariantBughouse &&\r
3162           gameInfo.variant != VariantCrazyhouse) break;\r
3163       if (x < 0 || y < 0) break;\r
3164       fromX = x;\r
3165       fromY = y;\r
3166       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
3167       SetupDropMenu(hmenu);\r
3168       MenuPopup(hwnd, pt, hmenu, -1);\r
3169       break;\r
3170     default:\r
3171       break;\r
3172     }\r
3173     break;\r
3174   }\r
3175 \r
3176   recursive--;\r
3177 }\r
3178 \r
3179 /* Preprocess messages for buttons in main window */\r
3180 LRESULT CALLBACK\r
3181 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3182 {\r
3183   int id = GetWindowLong(hwnd, GWL_ID);\r
3184   int i, dir;\r
3185 \r
3186   for (i=0; i<N_BUTTONS; i++) {\r
3187     if (buttonDesc[i].id == id) break;\r
3188   }\r
3189   if (i == N_BUTTONS) return 0;\r
3190   switch (message) {\r
3191   case WM_KEYDOWN:\r
3192     switch (wParam) {\r
3193     case VK_LEFT:\r
3194     case VK_RIGHT:\r
3195       dir = (wParam == VK_LEFT) ? -1 : 1;\r
3196       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
3197       return TRUE;\r
3198     }\r
3199     break;\r
3200   case WM_CHAR:\r
3201     switch (wParam) {\r
3202     case '\r':\r
3203       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
3204       return TRUE;\r
3205     case '\t':\r
3206       if (appData.icsActive) {\r
3207         if (GetKeyState(VK_SHIFT) < 0) {\r
3208           /* shifted */\r
3209           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3210           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3211           SetFocus(h);\r
3212         } else {\r
3213           /* unshifted */\r
3214           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3215           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3216           SetFocus(h);\r
3217         }\r
3218         return TRUE;\r
3219       }\r
3220       break;\r
3221     default:\r
3222       if (appData.icsActive) {\r
3223         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3224         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3225         SetFocus(h);\r
3226         SendMessage(h, WM_CHAR, wParam, lParam);\r
3227         return TRUE;\r
3228       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
3229         PopUpMoveDialog((char)wParam);\r
3230       }\r
3231       break;\r
3232     }\r
3233     break;\r
3234   }\r
3235   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
3236 }\r
3237 \r
3238 /* Process messages for Promotion dialog box */\r
3239 LRESULT CALLBACK\r
3240 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
3241 {\r
3242   char promoChar;\r
3243 \r
3244   switch (message) {\r
3245   case WM_INITDIALOG: /* message: initialize dialog box */\r
3246     /* Center the dialog over the application window */\r
3247     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
3248     ShowWindow(GetDlgItem(hDlg, PB_King), \r
3249       (!appData.testLegality || gameInfo.variant == VariantSuicide) ?\r
3250                SW_SHOW : SW_HIDE);\r
3251     return TRUE;\r
3252 \r
3253   case WM_COMMAND: /* message: received a command */\r
3254     switch (LOWORD(wParam)) {\r
3255     case IDCANCEL:\r
3256       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3257       ClearHighlights();\r
3258       DrawPosition(FALSE, NULL);\r
3259       return TRUE;\r
3260     case PB_King:\r
3261       promoChar = 'k';\r
3262       break;\r
3263     case PB_Queen:\r
3264       promoChar = 'q';\r
3265       break;\r
3266     case PB_Rook:\r
3267       promoChar = 'r';\r
3268       break;\r
3269     case PB_Bishop:\r
3270       promoChar = 'b';\r
3271       break;\r
3272     case PB_Knight:\r
3273       promoChar = 'n';\r
3274       break;\r
3275     default:\r
3276       return FALSE;\r
3277     }\r
3278     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
3279     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
3280     if (!appData.highlightLastMove) {\r
3281       ClearHighlights();\r
3282       DrawPosition(FALSE, NULL);\r
3283     }\r
3284     return TRUE;\r
3285   }\r
3286   return FALSE;\r
3287 }\r
3288 \r
3289 /* Pop up promotion dialog */\r
3290 VOID\r
3291 PromotionPopup(HWND hwnd)\r
3292 {\r
3293   FARPROC lpProc;\r
3294 \r
3295   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
3296   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
3297     hwnd, (DLGPROC)lpProc);\r
3298   FreeProcInstance(lpProc);\r
3299 }\r
3300 \r
3301 /* Toggle ShowThinking */\r
3302 VOID\r
3303 ToggleShowThinking()\r
3304 {\r
3305   ShowThinkingEvent(!appData.showThinking);\r
3306 }\r
3307 \r
3308 VOID\r
3309 LoadGameDialog(HWND hwnd, char* title)\r
3310 {\r
3311   UINT number = 0;\r
3312   FILE *f;\r
3313   char fileTitle[MSG_SIZ];\r
3314   f = OpenFileDialog(hwnd, FALSE, "",\r
3315                      appData.oldSaveStyle ? "gam" : "pgn",\r
3316                      GAME_FILT,\r
3317                      title, &number, fileTitle, NULL);\r
3318   if (f != NULL) {\r
3319     cmailMsgLoaded = FALSE;\r
3320     if (number == 0) {\r
3321       int error = GameListBuild(f);\r
3322       if (error) {\r
3323         DisplayError("Cannot build game list", error);\r
3324       } else if (!ListEmpty(&gameList) &&\r
3325                  ((ListGame *) gameList.tailPred)->number > 1) {\r
3326         GameListPopUp(f, fileTitle);\r
3327         return;\r
3328       }\r
3329       GameListDestroy();\r
3330       number = 1;\r
3331     }\r
3332     LoadGame(f, number, fileTitle, FALSE);\r
3333   }\r
3334 }\r
3335 \r
3336 VOID\r
3337 ChangedConsoleFont()\r
3338 {\r
3339   CHARFORMAT cfmt;\r
3340   CHARRANGE tmpsel, sel;\r
3341   MyFont *f = font[boardSize][CONSOLE_FONT];\r
3342   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3343   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3344   PARAFORMAT paraf;\r
3345 \r
3346   cfmt.cbSize = sizeof(CHARFORMAT);\r
3347   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
3348   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
3349   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
3350    * size.  This was undocumented in the version of MSVC++ that I had\r
3351    * when I wrote the code, but is apparently documented now.\r
3352    */\r
3353   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
3354   cfmt.bCharSet = f->lf.lfCharSet;\r
3355   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
3356   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
3357   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
3358   /* Why are the following seemingly needed too? */\r
3359   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
3360   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
3361   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
3362   tmpsel.cpMin = 0;\r
3363   tmpsel.cpMax = -1; /*999999?*/\r
3364   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
3365   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
3366   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
3367    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
3368    */\r
3369   paraf.cbSize = sizeof(paraf);\r
3370   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
3371   paraf.dxStartIndent = 0;\r
3372   paraf.dxOffset = WRAP_INDENT;\r
3373   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
3374   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
3375 }\r
3376 \r
3377 /*---------------------------------------------------------------------------*\\r
3378  *\r
3379  * Window Proc for main window\r
3380  *\r
3381 \*---------------------------------------------------------------------------*/\r
3382 \r
3383 /* Process messages for main window, etc. */\r
3384 LRESULT CALLBACK\r
3385 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
3386 {\r
3387   FARPROC lpProc;\r
3388   int wmId, wmEvent;\r
3389   char *defName;\r
3390   FILE *f;\r
3391   UINT number;\r
3392   char fileTitle[MSG_SIZ];\r
3393 \r
3394   switch (message) {\r
3395 \r
3396   case WM_PAINT: /* message: repaint portion of window */\r
3397     PaintProc(hwnd);\r
3398     break;\r
3399 \r
3400   case WM_ERASEBKGND:\r
3401     if (IsIconic(hwnd)) {\r
3402       /* Cheat; change the message */\r
3403       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
3404     } else {\r
3405       return (DefWindowProc(hwnd, message, wParam, lParam));\r
3406     }\r
3407     break;\r
3408 \r
3409   case WM_LBUTTONDOWN:\r
3410   case WM_MBUTTONDOWN:\r
3411   case WM_RBUTTONDOWN:\r
3412   case WM_LBUTTONUP:\r
3413   case WM_MBUTTONUP:\r
3414   case WM_RBUTTONUP:\r
3415   case WM_MOUSEMOVE:\r
3416     MouseEvent(hwnd, message, wParam, lParam);\r
3417     break;\r
3418 \r
3419   case WM_CHAR:\r
3420     \r
3421     if (appData.icsActive) {\r
3422       if (wParam == '\t') {\r
3423         if (GetKeyState(VK_SHIFT) < 0) {\r
3424           /* shifted */\r
3425           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3426           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3427           SetFocus(h);\r
3428         } else {\r
3429           /* unshifted */\r
3430           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
3431           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3432           SetFocus(h);\r
3433         }\r
3434       } else {\r
3435         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
3436         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
3437         SetFocus(h);\r
3438         SendMessage(h, message, wParam, lParam);\r
3439       }\r
3440     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
3441       PopUpMoveDialog((char)wParam);\r
3442     }\r
3443     break;\r
3444 \r
3445   case WM_PALETTECHANGED:\r
3446     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
3447       int nnew;\r
3448       HDC hdc = GetDC(hwndMain);\r
3449       SelectPalette(hdc, hPal, TRUE);\r
3450       nnew = RealizePalette(hdc);\r
3451       if (nnew > 0) {\r
3452         paletteChanged = TRUE;\r
3453 #if 0\r
3454         UpdateColors(hdc);\r
3455 #else\r
3456         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
3457 #endif\r
3458       }\r
3459       ReleaseDC(hwnd, hdc);\r
3460     }\r
3461     break;\r
3462 \r
3463   case WM_QUERYNEWPALETTE:\r
3464     if (!appData.monoMode /*&& paletteChanged*/) {\r
3465       int nnew;\r
3466       HDC hdc = GetDC(hwndMain);\r
3467       paletteChanged = FALSE;\r
3468       SelectPalette(hdc, hPal, FALSE);\r
3469       nnew = RealizePalette(hdc);\r
3470       if (nnew > 0) {\r
3471         InvalidateRect(hwnd, &boardRect, FALSE);\r
3472       }\r
3473       ReleaseDC(hwnd, hdc);\r
3474       return TRUE;\r
3475     }\r
3476     return FALSE;\r
3477 \r
3478   case WM_COMMAND: /* message: command from application menu */\r
3479     wmId    = LOWORD(wParam);\r
3480     wmEvent = HIWORD(wParam);\r
3481 \r
3482     switch (wmId) {\r
3483     case IDM_NewGame:\r
3484       ResetGameEvent();\r
3485       AnalysisPopDown();\r
3486       break;\r
3487 \r
3488     case IDM_LoadGame:\r
3489       LoadGameDialog(hwnd, "Load Game from File");\r
3490       break;\r
3491 \r
3492     case IDM_LoadNextGame:\r
3493       ReloadGame(1);\r
3494       break;\r
3495 \r
3496     case IDM_LoadPrevGame:\r
3497       ReloadGame(-1);\r
3498       break;\r
3499 \r
3500     case IDM_ReloadGame:\r
3501       ReloadGame(0);\r
3502       break;\r
3503 \r
3504     case IDM_LoadPosition:\r
3505       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
3506         Reset(FALSE, TRUE);\r
3507       }\r
3508       number = 1;\r
3509       f = OpenFileDialog(hwnd, FALSE, "",\r
3510                          appData.oldSaveStyle ? "pos" : "fen",\r
3511                          POSITION_FILT,\r
3512                          "Load Position from File", &number, fileTitle, NULL);\r
3513       if (f != NULL) {\r
3514         LoadPosition(f, number, fileTitle);\r
3515       }\r
3516       break;\r
3517 \r
3518     case IDM_LoadNextPosition:\r
3519       ReloadPosition(1);\r
3520       break;\r
3521 \r
3522     case IDM_LoadPrevPosition:\r
3523       ReloadPosition(-1);\r
3524       break;\r
3525 \r
3526     case IDM_ReloadPosition:\r
3527       ReloadPosition(0);\r
3528       break;\r
3529 \r
3530     case IDM_SaveGame:\r
3531       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
3532       f = OpenFileDialog(hwnd, TRUE, defName,\r
3533                          appData.oldSaveStyle ? "gam" : "pgn",\r
3534                          GAME_FILT,\r
3535                          "Save Game to File", NULL, fileTitle, NULL);\r
3536       if (f != NULL) {\r
3537         SaveGame(f, 0, "");\r
3538       }\r
3539       break;\r
3540 \r
3541     case IDM_SavePosition:\r
3542       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
3543       f = OpenFileDialog(hwnd, TRUE, defName,\r
3544                          appData.oldSaveStyle ? "pos" : "fen",\r
3545                          POSITION_FILT,\r
3546                          "Save Position to File", NULL, fileTitle, NULL);\r
3547       if (f != NULL) {\r
3548         SavePosition(f, 0, "");\r
3549       }\r
3550       break;\r
3551 \r
3552     case IDM_CopyGame:\r
3553       CopyGameToClipboard();\r
3554       break;\r
3555 \r
3556     case IDM_PasteGame:\r
3557       PasteGameFromClipboard();\r
3558       break;\r
3559 \r
3560     case IDM_CopyPosition:\r
3561       CopyFENToClipboard();\r
3562       break;\r
3563 \r
3564     case IDM_PastePosition:\r
3565       PasteFENFromClipboard();\r
3566       break;\r
3567 \r
3568     case IDM_MailMove:\r
3569       MailMoveEvent();\r
3570       break;\r
3571 \r
3572     case IDM_ReloadCMailMsg:\r
3573       Reset(TRUE, TRUE);\r
3574       ReloadCmailMsgEvent(FALSE);\r
3575       break;\r
3576 \r
3577     case IDM_Minimize:\r
3578       ShowWindow(hwnd, SW_MINIMIZE);\r
3579       break;\r
3580 \r
3581     case IDM_Exit:\r
3582       ExitEvent(0);\r
3583       break;\r
3584 \r
3585     case IDM_MachineWhite:\r
3586       MachineWhiteEvent();\r
3587       /*\r
3588        * refresh the tags dialog only if it's visible\r
3589        */\r
3590       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
3591           char *tags;\r
3592           tags = PGNTags(&gameInfo);\r
3593           TagsPopUp(tags, CmailMsg());\r
3594           free(tags);\r
3595       }\r
3596       break;\r
3597 \r
3598     case IDM_MachineBlack:\r
3599       MachineBlackEvent();\r
3600       /*\r
3601        * refresh the tags dialog only if it's visible\r
3602        */\r
3603       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
3604           char *tags;\r
3605           tags = PGNTags(&gameInfo);\r
3606           TagsPopUp(tags, CmailMsg());\r
3607           free(tags);\r
3608       }\r
3609       break;\r
3610 \r
3611     case IDM_TwoMachines:\r
3612       TwoMachinesEvent();\r
3613       /*\r
3614        * refresh the tags dialog only if it's visible\r
3615        */\r
3616       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
3617           char *tags;\r
3618           tags = PGNTags(&gameInfo);\r
3619           TagsPopUp(tags, CmailMsg());\r
3620           free(tags);\r
3621       }\r
3622       break;\r
3623 \r
3624     case IDM_AnalysisMode:\r
3625       if (!first.analysisSupport) {\r
3626         char buf[MSG_SIZ];\r
3627         sprintf(buf, "%s does not support analysis", first.tidy);\r
3628         DisplayError(buf, 0);\r
3629       } else {\r
3630         if (!appData.showThinking) ToggleShowThinking();\r
3631         AnalyzeModeEvent();\r
3632       }\r
3633       break;\r
3634 \r
3635     case IDM_AnalyzeFile:\r
3636       if (!first.analysisSupport) {\r
3637         char buf[MSG_SIZ];\r
3638         sprintf(buf, "%s does not support analysis", first.tidy);\r
3639         DisplayError(buf, 0);\r
3640       } else {\r
3641         if (!appData.showThinking) ToggleShowThinking();\r
3642         AnalyzeFileEvent();\r
3643         LoadGameDialog(hwnd, "Analyze Game from File");\r
3644         AnalysisPeriodicEvent(1);\r
3645       }\r
3646       break;\r
3647 \r
3648     case IDM_IcsClient:\r
3649       IcsClientEvent();\r
3650       break;\r
3651 \r
3652     case IDM_EditGame:\r
3653       EditGameEvent();\r
3654       break;\r
3655 \r
3656     case IDM_EditPosition:\r
3657       EditPositionEvent();\r
3658       break;\r
3659 \r
3660     case IDM_Training:\r
3661       TrainingEvent();\r
3662       break;\r
3663 \r
3664     case IDM_ShowGameList:\r
3665       ShowGameListProc();\r
3666       break;\r
3667 \r
3668     case IDM_EditTags:\r
3669       EditTagsProc();\r
3670       break;\r
3671 \r
3672     case IDM_EditComment:\r
3673       if (commentDialogUp && editComment) {\r
3674         CommentPopDown();\r
3675       } else {\r
3676         EditCommentEvent();\r
3677       }\r
3678       break;\r
3679 \r
3680     case IDM_Pause:\r
3681       PauseEvent();\r
3682       break;\r
3683 \r
3684     case IDM_Accept:\r
3685       AcceptEvent();\r
3686       break;\r
3687 \r
3688     case IDM_Decline:\r
3689       DeclineEvent();\r
3690       break;\r
3691 \r
3692     case IDM_Rematch:\r
3693       RematchEvent();\r
3694       break;\r
3695 \r
3696     case IDM_CallFlag:\r
3697       CallFlagEvent();\r
3698       break;\r
3699 \r
3700     case IDM_Draw:\r
3701       DrawEvent();\r
3702       break;\r
3703 \r
3704     case IDM_Adjourn:\r
3705       AdjournEvent();\r
3706       break;\r
3707 \r
3708     case IDM_Abort:\r
3709       AbortEvent();\r
3710       break;\r
3711 \r
3712     case IDM_Resign:\r
3713       ResignEvent();\r
3714       break;\r
3715 \r
3716     case IDM_StopObserving:\r
3717       StopObservingEvent();\r
3718       break;\r
3719 \r
3720     case IDM_StopExamining:\r
3721       StopExaminingEvent();\r
3722       break;\r
3723 \r
3724     case IDM_TypeInMove:\r
3725       PopUpMoveDialog('\000');\r
3726       break;\r
3727 \r
3728     case IDM_Backward:\r
3729       BackwardEvent();\r
3730       SetFocus(hwndMain);\r
3731       break;\r
3732 \r
3733     case IDM_Forward:\r
3734       ForwardEvent();\r
3735       SetFocus(hwndMain);\r
3736       break;\r
3737 \r
3738     case IDM_ToStart:\r
3739       ToStartEvent();\r
3740       SetFocus(hwndMain);\r
3741       break;\r
3742 \r
3743     case IDM_ToEnd:\r
3744       ToEndEvent();\r
3745       SetFocus(hwndMain);\r
3746       break;\r
3747 \r
3748     case IDM_Revert:\r
3749       RevertEvent();\r
3750       break;\r
3751 \r
3752     case IDM_TruncateGame:\r
3753       TruncateGameEvent();\r
3754       break;\r
3755 \r
3756     case IDM_MoveNow:\r
3757       MoveNowEvent();\r
3758       break;\r
3759 \r
3760     case IDM_RetractMove:\r
3761       RetractMoveEvent();\r
3762       break;\r
3763 \r
3764     case IDM_FlipView:\r
3765       flipView = !flipView;\r
3766       DrawPosition(FALSE, NULL);\r
3767       break;\r
3768 \r
3769     case IDM_GeneralOptions:\r
3770       GeneralOptionsPopup(hwnd);\r
3771       break;\r
3772 \r
3773     case IDM_BoardOptions:\r
3774       BoardOptionsPopup(hwnd);\r
3775       break;\r
3776 \r
3777     case IDM_IcsOptions:\r
3778       IcsOptionsPopup(hwnd);\r
3779       break;\r
3780 \r
3781     case IDM_Fonts:\r
3782       FontsOptionsPopup(hwnd);\r
3783       break;\r
3784 \r
3785     case IDM_Sounds:\r
3786       SoundOptionsPopup(hwnd);\r
3787       break;\r
3788 \r
3789     case IDM_CommPort:\r
3790       CommPortOptionsPopup(hwnd);\r
3791       break;\r
3792 \r
3793     case IDM_LoadOptions:\r
3794       LoadOptionsPopup(hwnd);\r
3795       break;\r
3796 \r
3797     case IDM_SaveOptions:\r
3798       SaveOptionsPopup(hwnd);\r
3799       break;\r
3800 \r
3801     case IDM_TimeControl:\r
3802       TimeControlOptionsPopup(hwnd);\r
3803       break;\r
3804 \r
3805     case IDM_SaveSettings:\r
3806       SaveSettings(settingsFileName);\r
3807       break;\r
3808 \r
3809     case IDM_SaveSettingsOnExit:\r
3810       saveSettingsOnExit = !saveSettingsOnExit;\r
3811       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
3812                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
3813                                          MF_CHECKED : MF_UNCHECKED));\r
3814       break;\r
3815 \r
3816     case IDM_Hint:\r
3817       HintEvent();\r
3818       break;\r
3819 \r
3820     case IDM_Book:\r
3821       BookEvent();\r
3822       break;\r
3823 \r
3824     case IDM_AboutGame:\r
3825       AboutGameEvent();\r
3826       break;\r
3827 \r
3828     case IDM_Debug:\r
3829       appData.debugMode = !appData.debugMode;\r
3830       if (appData.debugMode) {\r
3831         char dir[MSG_SIZ];\r
3832         GetCurrentDirectory(MSG_SIZ, dir);\r
3833         SetCurrentDirectory(installDir);\r
3834         debugFP = fopen("WinBoard.debug", "w");\r
3835         SetCurrentDirectory(dir);\r
3836         setbuf(debugFP, NULL);\r
3837       } else {\r
3838         fclose(debugFP);\r
3839         debugFP = NULL;\r
3840       }\r
3841       break;\r
3842 \r
3843     case IDM_HELPCONTENTS:\r
3844       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
3845         MessageBox (GetFocus(),\r
3846                     "Unable to activate help",\r
3847                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
3848       }\r
3849       break;\r
3850 \r
3851     case IDM_HELPSEARCH:\r
3852       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {\r
3853         MessageBox (GetFocus(),\r
3854                     "Unable to activate help",\r
3855                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
3856       }\r
3857       break;\r
3858 \r
3859     case IDM_HELPHELP:\r
3860       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
3861         MessageBox (GetFocus(),\r
3862                     "Unable to activate help",\r
3863                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
3864       }\r
3865       break;\r
3866 \r
3867     case IDM_ABOUT:\r
3868       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
3869       DialogBox(hInst, \r
3870         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
3871         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
3872       FreeProcInstance(lpProc);\r
3873       break;\r
3874 \r
3875     case IDM_DirectCommand1:\r
3876       AskQuestionEvent("Direct Command",\r
3877                        "Send to chess program:", "", "1");\r
3878       break;\r
3879     case IDM_DirectCommand2:\r
3880       AskQuestionEvent("Direct Command",\r
3881                        "Send to second chess program:", "", "2");\r
3882       break;\r
3883 \r
3884     case EP_WhitePawn:\r
3885       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
3886       fromX = fromY = -1;\r
3887       break;\r
3888 \r
3889     case EP_WhiteKnight:\r
3890       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
3891       fromX = fromY = -1;\r
3892       break;\r
3893 \r
3894     case EP_WhiteBishop:\r
3895       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
3896       fromX = fromY = -1;\r
3897       break;\r
3898 \r
3899     case EP_WhiteRook:\r
3900       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
3901       fromX = fromY = -1;\r
3902       break;\r
3903 \r
3904     case EP_WhiteQueen:\r
3905       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
3906       fromX = fromY = -1;\r
3907       break;\r
3908 \r
3909     case EP_WhiteKing:\r
3910       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
3911       fromX = fromY = -1;\r
3912       break;\r
3913 \r
3914     case EP_BlackPawn:\r
3915       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
3916       fromX = fromY = -1;\r
3917       break;\r
3918 \r
3919     case EP_BlackKnight:\r
3920       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
3921       fromX = fromY = -1;\r
3922       break;\r
3923 \r
3924     case EP_BlackBishop:\r
3925       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
3926       fromX = fromY = -1;\r
3927       break;\r
3928 \r
3929     case EP_BlackRook:\r
3930       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
3931       fromX = fromY = -1;\r
3932       break;\r
3933 \r
3934     case EP_BlackQueen:\r
3935       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
3936       fromX = fromY = -1;\r
3937       break;\r
3938 \r
3939     case EP_BlackKing:\r
3940       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
3941       fromX = fromY = -1;\r
3942       break;\r
3943 \r
3944     case EP_EmptySquare:\r
3945       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
3946       fromX = fromY = -1;\r
3947       break;\r
3948 \r
3949     case EP_ClearBoard:\r
3950       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
3951       fromX = fromY = -1;\r
3952       break;\r
3953 \r
3954     case EP_White:\r
3955       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
3956       fromX = fromY = -1;\r
3957       break;\r
3958 \r
3959     case EP_Black:\r
3960       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
3961       fromX = fromY = -1;\r
3962       break;\r
3963 \r
3964     case DP_Pawn:\r
3965       DropMenuEvent(WhitePawn, fromX, fromY);\r
3966       fromX = fromY = -1;\r
3967       break;\r
3968 \r
3969     case DP_Knight:\r
3970       DropMenuEvent(WhiteKnight, fromX, fromY);\r
3971       fromX = fromY = -1;\r
3972       break;\r
3973 \r
3974     case DP_Bishop:\r
3975       DropMenuEvent(WhiteBishop, fromX, fromY);\r
3976       fromX = fromY = -1;\r
3977       break;\r
3978 \r
3979     case DP_Rook:\r
3980       DropMenuEvent(WhiteRook, fromX, fromY);\r
3981       fromX = fromY = -1;\r
3982       break;\r
3983 \r
3984     case DP_Queen:\r
3985       DropMenuEvent(WhiteQueen, fromX, fromY);\r
3986       fromX = fromY = -1;\r
3987       break;\r
3988 \r
3989     default:\r
3990       return (DefWindowProc(hwnd, message, wParam, lParam));\r
3991     }\r
3992     break;\r
3993 \r
3994   case WM_TIMER:\r
3995     switch (wParam) {\r
3996     case CLOCK_TIMER_ID:\r
3997       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
3998       clockTimerEvent = 0;\r
3999       DecrementClocks(); /* call into back end */\r
4000       break;\r
4001     case LOAD_GAME_TIMER_ID:\r
4002       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
4003       loadGameTimerEvent = 0;\r
4004       AutoPlayGameLoop(); /* call into back end */\r
4005       break;\r
4006     case ANALYSIS_TIMER_ID:\r
4007       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && \r
4008           appData.periodicUpdates) {\r
4009         AnalysisPeriodicEvent(0);\r
4010       } else {\r
4011         KillTimer(hwnd, analysisTimerEvent);\r
4012         analysisTimerEvent = 0;\r
4013       }\r
4014       break;\r
4015     case DELAYED_TIMER_ID:\r
4016       KillTimer(hwnd, delayedTimerEvent);\r
4017       delayedTimerEvent = 0;\r
4018       delayedTimerCallback();\r
4019       break;\r
4020     }\r
4021     break;\r
4022 \r
4023   case WM_USER_Input:\r
4024     InputEvent(hwnd, message, wParam, lParam);\r
4025     break;\r
4026 \r
4027   case WM_ENTERSIZEMOVE:\r
4028     if (hwnd == hwndMain) {\r
4029       doingSizing = TRUE;\r
4030       lastSizing = 0;\r
4031     }\r
4032     break;\r
4033 \r
4034   case WM_SIZING:\r
4035     if (hwnd == hwndMain) {\r
4036       lastSizing = wParam;\r
4037     }\r
4038     break;\r
4039 \r
4040   case WM_EXITSIZEMOVE:\r
4041     if (hwnd == hwndMain) {\r
4042       RECT client;\r
4043       doingSizing = FALSE;\r
4044       InvalidateRect(hwnd, &boardRect, FALSE);\r
4045       GetClientRect(hwnd, &client);\r
4046       ResizeBoard(client.right, client.bottom, lastSizing);\r
4047       lastSizing = 0;\r
4048     }\r
4049     break;\r
4050 \r
4051   case WM_DESTROY: /* message: window being destroyed */\r
4052     PostQuitMessage(0);\r
4053     break;\r
4054 \r
4055   case WM_CLOSE:\r
4056     if (hwnd == hwndMain) {\r
4057       ExitEvent(0);\r
4058     }\r
4059     break;\r
4060 \r
4061   default:      /* Passes it on if unprocessed */\r
4062     return (DefWindowProc(hwnd, message, wParam, lParam));\r
4063   }\r
4064   return 0;\r
4065 }\r
4066 \r
4067 /*---------------------------------------------------------------------------*\\r
4068  *\r
4069  * Misc utility routines\r
4070  *\r
4071 \*---------------------------------------------------------------------------*/\r
4072 \r
4073 /* \r
4074  * returns TRUE if user selects a different color, FALSE otherwise \r
4075  */\r
4076 \r
4077 BOOL\r
4078 ChangeColor(HWND hwnd, COLORREF *which)\r
4079 {\r
4080   static BOOL firstTime = TRUE;\r
4081   static DWORD customColors[16];\r
4082   CHOOSECOLOR cc;\r
4083   COLORREF newcolor;\r
4084   int i;\r
4085   ColorClass ccl;\r
4086 \r
4087   if (firstTime) {\r
4088     /* Make initial colors in use available as custom colors */\r
4089     /* Should we put the compiled-in defaults here instead? */\r
4090     i = 0;\r
4091     customColors[i++] = lightSquareColor & 0xffffff;\r
4092     customColors[i++] = darkSquareColor & 0xffffff;\r
4093     customColors[i++] = whitePieceColor & 0xffffff;\r
4094     customColors[i++] = blackPieceColor & 0xffffff;\r
4095     customColors[i++] = highlightSquareColor & 0xffffff;\r
4096     customColors[i++] = premoveHighlightColor & 0xffffff;\r
4097 \r
4098     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
4099       customColors[i++] = textAttribs[ccl].color;\r
4100     }\r
4101     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
4102     firstTime = FALSE;\r
4103   }\r
4104 \r
4105   cc.lStructSize = sizeof(cc);\r
4106   cc.hwndOwner = hwnd;\r
4107   cc.hInstance = NULL;\r
4108   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
4109   cc.lpCustColors = (LPDWORD) customColors;\r
4110   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
4111 \r
4112   if (!ChooseColor(&cc)) return FALSE;\r
4113 \r
4114   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
4115   if (newcolor == *which) return FALSE;\r
4116   *which = newcolor;\r
4117   return TRUE;\r
4118 \r
4119   /*\r
4120   InitDrawingColors();\r
4121   InvalidateRect(hwnd, &boardRect, FALSE);\r
4122   */\r
4123 }\r
4124 \r
4125 BOOLEAN\r
4126 MyLoadSound(MySound *ms)\r
4127 {\r
4128   BOOL ok = FALSE;\r
4129   struct stat st;\r
4130   FILE *f;\r
4131 \r
4132   if (ms->data) free(ms->data);\r
4133   ms->data = NULL;\r
4134 \r
4135   switch (ms->name[0]) {\r
4136   case NULLCHAR:\r
4137     /* Silence */\r
4138     ok = TRUE;\r
4139     break;\r
4140   case '$':\r
4141     /* System sound from Control Panel.  Don't preload here. */\r
4142     ok = TRUE;\r
4143     break;\r
4144   case '!':\r
4145     if (ms->name[1] == NULLCHAR) {\r
4146       /* "!" alone = silence */\r
4147       ok = TRUE;\r
4148     } else {\r
4149       /* Builtin wave resource.  Error if not found. */\r
4150       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
4151       if (h == NULL) break;\r
4152       ms->data = (void *)LoadResource(hInst, h);\r
4153       if (h == NULL) break;\r
4154       ok = TRUE;\r
4155     }\r
4156     break;\r
4157   default:\r
4158     /* .wav file.  Error if not found. */\r
4159     f = fopen(ms->name, "rb");\r
4160     if (f == NULL) break;\r
4161     if (fstat(fileno(f), &st) < 0) break;\r
4162     ms->data = malloc(st.st_size);\r
4163     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
4164     fclose(f);\r
4165     ok = TRUE;\r
4166     break;\r
4167   }\r
4168   if (!ok) {\r
4169     char buf[MSG_SIZ];\r
4170     sprintf(buf, "Error loading sound %s", ms->name);\r
4171     DisplayError(buf, GetLastError());\r
4172   }\r
4173   return ok;\r
4174 }\r
4175 \r
4176 BOOLEAN\r
4177 MyPlaySound(MySound *ms)\r
4178 {\r
4179   BOOLEAN ok = FALSE;\r
4180   switch (ms->name[0]) {\r
4181   case NULLCHAR:\r
4182     /* Silence */\r
4183     ok = TRUE;\r
4184     break;\r
4185   case '$':\r
4186     /* System sound from Control Panel (deprecated feature).\r
4187        "$" alone or an unset sound name gets default beep (still in use). */\r
4188     if (ms->name[1]) {\r
4189       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
4190     }\r
4191     if (!ok) ok = MessageBeep(MB_OK);\r
4192     break; \r
4193   case '!':\r
4194     /* Builtin wave resource, or "!" alone for silence */\r
4195     if (ms->name[1]) {\r
4196       if (ms->data == NULL) return FALSE;\r
4197       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
4198     } else {\r
4199       ok = TRUE;\r
4200     }\r
4201     break;\r
4202   default:\r
4203     /* .wav file.  Error if not found. */\r
4204     if (ms->data == NULL) return FALSE;\r
4205     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
4206     break;\r
4207   }\r
4208   /* Don't print an error: this can happen innocently if the sound driver\r
4209      is busy; for instance, if another instance of WinBoard is playing\r
4210      a sound at about the same time. */\r
4211 #if 0\r
4212   if (!ok) {\r
4213     char buf[MSG_SIZ];\r
4214     sprintf(buf, "Error playing sound %s", ms->name);\r
4215     DisplayError(buf, GetLastError());\r
4216   }\r
4217 #endif\r
4218   return ok;\r
4219 }\r
4220 \r
4221 \r
4222 LRESULT CALLBACK\r
4223 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4224 {\r
4225   BOOL ok;\r
4226   OPENFILENAME *ofn;\r
4227   static UINT *number; /* gross that this is static */\r
4228 \r
4229   switch (message) {\r
4230   case WM_INITDIALOG: /* message: initialize dialog box */\r
4231     /* Center the dialog over the application window */\r
4232     ofn = (OPENFILENAME *) lParam;\r
4233     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
4234       number = (UINT *) ofn->lCustData;\r
4235       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
4236     } else {\r
4237       number = NULL;\r
4238     }\r
4239     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
4240     return FALSE;  /* Allow for further processing */\r
4241 \r
4242   case WM_COMMAND:\r
4243     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
4244       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
4245     }\r
4246     return FALSE;  /* Allow for further processing */\r
4247   }\r
4248   return FALSE;\r
4249 }\r
4250 \r
4251 UINT APIENTRY\r
4252 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
4253 {\r
4254   static UINT *number;\r
4255   OPENFILENAME *ofname;\r
4256   OFNOTIFY *ofnot;\r
4257   switch (uiMsg) {\r
4258   case WM_INITDIALOG:\r
4259     ofname = (OPENFILENAME *)lParam;\r
4260     number = (UINT *)(ofname->lCustData);\r
4261     break;\r
4262   case WM_NOTIFY:\r
4263     ofnot = (OFNOTIFY *)lParam;\r
4264     if (ofnot->hdr.code == CDN_FILEOK) {\r
4265       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
4266     }\r
4267     break;\r
4268   }\r
4269   return 0;\r
4270 }\r
4271 \r
4272 \r
4273 FILE *\r
4274 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,\r
4275                char *nameFilt, char *dlgTitle, UINT *number,\r
4276                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
4277 {\r
4278   OPENFILENAME openFileName;\r
4279   char buf1[MSG_SIZ];\r
4280   FILE *f;\r
4281 \r
4282   if (fileName == NULL) fileName = buf1;\r
4283   if (defName == NULL) {\r
4284     strcpy(fileName, "*.");\r
4285     strcat(fileName, defExt);\r
4286   } else {\r
4287     strcpy(fileName, defName);\r
4288   }\r
4289   if (fileTitle) strcpy(fileTitle, "");\r
4290   if (number) *number = 0;\r
4291 \r
4292   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
4293   openFileName.hwndOwner         = hwnd;\r
4294   openFileName.hInstance         = (HANDLE) hInst;\r
4295   openFileName.lpstrFilter       = nameFilt;\r
4296   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
4297   openFileName.nMaxCustFilter    = 0L;\r
4298   openFileName.nFilterIndex      = 1L;\r
4299   openFileName.lpstrFile         = fileName;\r
4300   openFileName.nMaxFile          = MSG_SIZ;\r
4301   openFileName.lpstrFileTitle    = fileTitle;\r
4302   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
4303   openFileName.lpstrInitialDir   = NULL;\r
4304   openFileName.lpstrTitle        = dlgTitle;\r
4305   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
4306     | (write ? 0 : OFN_FILEMUSTEXIST) \r
4307     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
4308     | (oldDialog ? 0 : OFN_EXPLORER);\r
4309   openFileName.nFileOffset       = 0;\r
4310   openFileName.nFileExtension    = 0;\r
4311   openFileName.lpstrDefExt       = defExt;\r
4312   openFileName.lCustData         = (LONG) number;\r
4313   openFileName.lpfnHook          = oldDialog ?\r
4314     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
4315   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
4316 \r
4317   if (write ? GetSaveFileName(&openFileName) : \r
4318               GetOpenFileName(&openFileName)) {\r
4319     /* open the file */\r
4320     f = fopen(openFileName.lpstrFile, write ? "a" : "r");\r
4321     if (f == NULL) {\r
4322       MessageBox(hwnd, "File open failed", NULL,\r
4323                  MB_OK|MB_ICONEXCLAMATION);\r
4324       return NULL;\r
4325     }\r
4326   } else {\r
4327     int err = CommDlgExtendedError();\r
4328     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
4329     return FALSE;\r
4330   }\r
4331   return f;\r
4332 }\r
4333 \r
4334 \r
4335 \r
4336 VOID APIENTRY\r
4337 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
4338 {\r
4339   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
4340 \r
4341   /*\r
4342    * Get the first pop-up menu in the menu template. This is the\r
4343    * menu that TrackPopupMenu displays.\r
4344    */\r
4345   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
4346 \r
4347   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
4348 \r
4349   /*\r
4350    * TrackPopup uses screen coordinates, so convert the\r
4351    * coordinates of the mouse click to screen coordinates.\r
4352    */\r
4353   ClientToScreen(hwnd, (LPPOINT) &pt);\r
4354 \r
4355   /* Draw and track the floating pop-up menu. */\r
4356   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
4357                  pt.x, pt.y, 0, hwnd, NULL);\r
4358 \r
4359   /* Destroy the menu.*/\r
4360   DestroyMenu(hmenu);\r
4361 }\r
4362    \r
4363 typedef struct {\r
4364   HWND hDlg, hText;\r
4365   int sizeX, sizeY, newSizeX, newSizeY;\r
4366   HDWP hdwp;\r
4367 } ResizeEditPlusButtonsClosure;\r
4368 \r
4369 BOOL CALLBACK\r
4370 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
4371 {\r
4372   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
4373   RECT rect;\r
4374   POINT pt;\r
4375 \r
4376   if (hChild == cl->hText) return TRUE;\r
4377   GetWindowRect(hChild, &rect); /* gives screen coords */\r
4378   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
4379   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
4380   ScreenToClient(cl->hDlg, &pt);\r
4381   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
4382     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
4383   return TRUE;\r
4384 }\r
4385 \r
4386 /* Resize a dialog that has a (rich) edit field filling most of\r
4387    the top, with a row of buttons below */\r
4388 VOID\r
4389 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
4390 {\r
4391   RECT rectText;\r
4392   int newTextHeight, newTextWidth;\r
4393   ResizeEditPlusButtonsClosure cl;\r
4394   \r
4395   /*if (IsIconic(hDlg)) return;*/\r
4396   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
4397   \r
4398   cl.hdwp = BeginDeferWindowPos(8);\r
4399 \r
4400   GetWindowRect(hText, &rectText); /* gives screen coords */\r
4401   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
4402   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
4403   if (newTextHeight < 0) {\r
4404     newSizeY += -newTextHeight;\r
4405     newTextHeight = 0;\r
4406   }\r
4407   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
4408     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
4409 \r
4410   cl.hDlg = hDlg;\r
4411   cl.hText = hText;\r
4412   cl.sizeX = sizeX;\r
4413   cl.sizeY = sizeY;\r
4414   cl.newSizeX = newSizeX;\r
4415   cl.newSizeY = newSizeY;\r
4416   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
4417 \r
4418   EndDeferWindowPos(cl.hdwp);\r
4419 }\r
4420 \r
4421 /* Center one window over another */\r
4422 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
4423 {\r
4424     RECT    rChild, rParent;\r
4425     int     wChild, hChild, wParent, hParent;\r
4426     int     wScreen, hScreen, xNew, yNew;\r
4427     HDC     hdc;\r
4428 \r
4429     /* Get the Height and Width of the child window */\r
4430     GetWindowRect (hwndChild, &rChild);\r
4431     wChild = rChild.right - rChild.left;\r
4432     hChild = rChild.bottom - rChild.top;\r
4433 \r
4434     /* Get the Height and Width of the parent window */\r
4435     GetWindowRect (hwndParent, &rParent);\r
4436     wParent = rParent.right - rParent.left;\r
4437     hParent = rParent.bottom - rParent.top;\r
4438 \r
4439     /* Get the display limits */\r
4440     hdc = GetDC (hwndChild);\r
4441     wScreen = GetDeviceCaps (hdc, HORZRES);\r
4442     hScreen = GetDeviceCaps (hdc, VERTRES);\r
4443     ReleaseDC(hwndChild, hdc);\r
4444 \r
4445     /* Calculate new X position, then adjust for screen */\r
4446     xNew = rParent.left + ((wParent - wChild) /2);\r
4447     if (xNew < 0) {\r
4448         xNew = 0;\r
4449     } else if ((xNew+wChild) > wScreen) {\r
4450         xNew = wScreen - wChild;\r
4451     }\r
4452 \r
4453     /* Calculate new Y position, then adjust for screen */\r
4454     yNew = rParent.top  + ((hParent - hChild) /2);\r
4455     if (yNew < 0) {\r
4456         yNew = 0;\r
4457     } else if ((yNew+hChild) > hScreen) {\r
4458         yNew = hScreen - hChild;\r
4459     }\r
4460 \r
4461     /* Set it, and return */\r
4462     return SetWindowPos (hwndChild, NULL,\r
4463                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
4464 }\r
4465 \r
4466 /*---------------------------------------------------------------------------*\\r
4467  *\r
4468  * Startup Dialog functions\r
4469  *\r
4470 \*---------------------------------------------------------------------------*/\r
4471 void\r
4472 InitComboStrings(HANDLE hwndCombo, char **cd)\r
4473 {\r
4474   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
4475 \r
4476   while (*cd != NULL) {\r
4477     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
4478     cd++;\r
4479   }\r
4480 }\r
4481 \r
4482 void\r
4483 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
4484 {\r
4485   char buf1[ARG_MAX];\r
4486   int len;\r
4487 \r
4488   if (str[0] == '@') {\r
4489     FILE* f = fopen(str + 1, "r");\r
4490     if (f == NULL) {\r
4491       DisplayFatalError(str + 1, errno, 2);\r
4492       return;\r
4493     }\r
4494     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
4495     fclose(f);\r
4496     buf1[len] = NULLCHAR;\r
4497     str = buf1;\r
4498   }\r
4499 \r
4500   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
4501 \r
4502   for (;;) {\r
4503     char buf[MSG_SIZ];\r
4504     char *end = strchr(str, '\n');\r
4505     if (end == NULL) return;\r
4506     memcpy(buf, str, end - str);\r
4507     buf[end - str] = NULLCHAR;\r
4508     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
4509     str = end + 1;\r
4510   }\r
4511 }\r
4512 \r
4513 void\r
4514 SetStartupDialogEnables(HWND hDlg)\r
4515 {\r
4516   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
4517     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
4518     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
4519   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
4520     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
4521   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
4522     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
4523   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
4524     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
4525   EnableWindow(GetDlgItem(hDlg, IDOK),\r
4526     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
4527     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
4528     IsDlgButtonChecked(hDlg, OPT_View));\r
4529 }\r
4530 \r
4531 char *\r
4532 QuoteForFilename(char *filename)\r
4533 {\r
4534   int dquote, space;\r
4535   dquote = strchr(filename, '"') != NULL;\r
4536   space = strchr(filename, ' ') != NULL;\r
4537   if (dquote || space) {\r
4538     if (dquote) {\r
4539       return "'";\r
4540     } else {\r
4541       return "\"";\r
4542     }\r
4543   } else {\r
4544     return "";\r
4545   }\r
4546 }\r
4547 \r
4548 VOID\r
4549 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
4550 {\r
4551   char buf[MSG_SIZ];\r
4552   char *q;\r
4553 \r
4554   InitComboStringsFromOption(hwndCombo, nthnames);\r
4555   q = QuoteForFilename(nthcp);\r
4556   sprintf(buf, "%s%s%s", q, nthcp, q);\r
4557   if (*nthdir != NULLCHAR) {\r
4558     q = QuoteForFilename(nthdir);\r
4559     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
4560   }\r
4561   if (*nthcp == NULLCHAR) {\r
4562     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
4563   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
4564     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
4565     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
4566   }\r
4567 }\r
4568 \r
4569 LRESULT CALLBACK\r
4570 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4571 {\r
4572   char buf[MSG_SIZ];\r
4573   HANDLE hwndCombo;\r
4574   char *p;\r
4575 \r
4576   switch (message) {\r
4577   case WM_INITDIALOG:\r
4578     /* Center the dialog */\r
4579     CenterWindow (hDlg, GetDesktopWindow());\r
4580     /* Initialize the dialog items */\r
4581     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
4582                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
4583                   firstChessProgramNames);\r
4584     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
4585                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
4586                   secondChessProgramNames);\r
4587     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
4588     InitComboStringsFromOption(hwndCombo, icsNames);    \r
4589     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
4590     if (*appData.icsHelper != NULLCHAR) {\r
4591       char *q = QuoteForFilename(appData.icsHelper);\r
4592       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
4593     }\r
4594     if (*appData.icsHost == NULLCHAR) {\r
4595       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
4596       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
4597     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
4598       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
4599       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
4600     }\r
4601     if (chessProgram) {\r
4602       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
4603     } else if (appData.icsActive) {\r
4604       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
4605     } else if (appData.noChessProgram) {\r
4606       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
4607     }\r
4608     SetStartupDialogEnables(hDlg);\r
4609     return TRUE;\r
4610 \r
4611   case WM_COMMAND:\r
4612     switch (LOWORD(wParam)) {\r
4613     case IDOK:\r
4614       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
4615         strcpy(buf, "/fcp=");\r
4616         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
4617         p = buf;\r
4618         ParseArgs(StringGet, &p);\r
4619         strcpy(buf, "/scp=");\r
4620         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
4621         p = buf;\r
4622         ParseArgs(StringGet, &p);\r
4623         appData.noChessProgram = FALSE;\r
4624         appData.icsActive = FALSE;\r
4625       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
4626         strcpy(buf, "/ics /icshost=");\r
4627         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
4628         p = buf;\r
4629         ParseArgs(StringGet, &p);\r
4630         if (appData.zippyPlay) {\r
4631           strcpy(buf, "/fcp=");\r
4632           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
4633           p = buf;\r
4634           ParseArgs(StringGet, &p);\r
4635         }\r
4636       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
4637         appData.noChessProgram = TRUE;\r
4638         appData.icsActive = FALSE;\r
4639       } else {\r
4640         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
4641                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
4642         return TRUE;\r
4643       }\r
4644       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
4645         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
4646         p = buf;\r
4647         ParseArgs(StringGet, &p);\r
4648       }\r
4649       EndDialog(hDlg, TRUE);\r
4650       return TRUE;\r
4651 \r
4652     case IDCANCEL:\r
4653       ExitEvent(0);\r
4654       return TRUE;\r
4655 \r
4656     case IDM_HELPCONTENTS:\r
4657       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
4658         MessageBox (GetFocus(),\r
4659                     "Unable to activate help",\r
4660                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
4661       }\r
4662       break;\r
4663 \r
4664     default:\r
4665       SetStartupDialogEnables(hDlg);\r
4666       break;\r
4667     }\r
4668     break;\r
4669   }\r
4670   return FALSE;\r
4671 }\r
4672 \r
4673 /*---------------------------------------------------------------------------*\\r
4674  *\r
4675  * About box dialog functions\r
4676  *\r
4677 \*---------------------------------------------------------------------------*/\r
4678 \r
4679 /* Process messages for "About" dialog box */\r
4680 LRESULT CALLBACK\r
4681 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4682 {\r
4683   switch (message) {\r
4684   case WM_INITDIALOG: /* message: initialize dialog box */\r
4685     /* Center the dialog over the application window */\r
4686     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
4687     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
4688     return (TRUE);\r
4689 \r
4690   case WM_COMMAND: /* message: received a command */\r
4691     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
4692         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
4693       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
4694       return (TRUE);\r
4695     }\r
4696     break;\r
4697   }\r
4698   return (FALSE);\r
4699 }\r
4700 \r
4701 /*---------------------------------------------------------------------------*\\r
4702  *\r
4703  * Comment Dialog functions\r
4704  *\r
4705 \*---------------------------------------------------------------------------*/\r
4706 \r
4707 LRESULT CALLBACK\r
4708 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4709 {\r
4710   static HANDLE hwndText = NULL;\r
4711   int len, newSizeX, newSizeY, flags;\r
4712   static int sizeX, sizeY;\r
4713   char *str;\r
4714   RECT rect;\r
4715   MINMAXINFO *mmi;\r
4716 \r
4717   switch (message) {\r
4718   case WM_INITDIALOG: /* message: initialize dialog box */\r
4719     /* Initialize the dialog items */\r
4720     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
4721     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
4722     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
4723     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
4724     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
4725     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
4726     SetWindowText(hDlg, commentTitle);\r
4727     if (editComment) {\r
4728       SetFocus(hwndText);\r
4729     } else {\r
4730       SetFocus(GetDlgItem(hDlg, IDOK));\r
4731     }\r
4732     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
4733                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
4734                 MAKELPARAM(FALSE, 0));\r
4735     /* Size and position the dialog */\r
4736     if (!commentDialog) {\r
4737       commentDialog = hDlg;\r
4738       flags = SWP_NOZORDER;\r
4739       GetClientRect(hDlg, &rect);\r
4740       sizeX = rect.right;\r
4741       sizeY = rect.bottom;\r
4742       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
4743           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
4744         WINDOWPLACEMENT wp;\r
4745         EnsureOnScreen(&commentX, &commentY);\r
4746         wp.length = sizeof(WINDOWPLACEMENT);\r
4747         wp.flags = 0;\r
4748         wp.showCmd = SW_SHOW;\r
4749         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
4750         wp.rcNormalPosition.left = commentX;\r
4751         wp.rcNormalPosition.right = commentX + commentW;\r
4752         wp.rcNormalPosition.top = commentY;\r
4753         wp.rcNormalPosition.bottom = commentY + commentH;\r
4754         SetWindowPlacement(hDlg, &wp);\r
4755 \r
4756         GetClientRect(hDlg, &rect);\r
4757         newSizeX = rect.right;\r
4758         newSizeY = rect.bottom;\r
4759         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
4760                               newSizeX, newSizeY);\r
4761         sizeX = newSizeX;\r
4762         sizeY = newSizeY;\r
4763       }\r
4764     }\r
4765     return FALSE;\r
4766 \r
4767   case WM_COMMAND: /* message: received a command */\r
4768     switch (LOWORD(wParam)) {\r
4769     case IDOK:\r
4770       if (editComment) {\r
4771         char *p, *q;\r
4772         /* Read changed options from the dialog box */\r
4773         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
4774         len = GetWindowTextLength(hwndText);\r
4775         str = (char *) malloc(len + 1);\r
4776         GetWindowText(hwndText, str, len + 1);\r
4777         p = q = str;\r
4778         while (*q) {\r
4779           if (*q == '\r')\r
4780             q++;\r
4781           else\r
4782             *p++ = *q++;\r
4783         }\r
4784         *p = NULLCHAR;\r
4785         ReplaceComment(commentIndex, str);\r
4786         free(str);\r
4787       }\r
4788       CommentPopDown();\r
4789       return TRUE;\r
4790 \r
4791     case IDCANCEL:\r
4792     case OPT_CancelComment:\r
4793       CommentPopDown();\r
4794       return TRUE;\r
4795 \r
4796     case OPT_ClearComment:\r
4797       SetDlgItemText(hDlg, OPT_CommentText, "");\r
4798       break;\r
4799 \r
4800     case OPT_EditComment:\r
4801       EditCommentEvent();\r
4802       return TRUE;\r
4803 \r
4804     default:\r
4805       break;\r
4806     }\r
4807     break;\r
4808 \r
4809   case WM_SIZE:\r
4810     newSizeX = LOWORD(lParam);\r
4811     newSizeY = HIWORD(lParam);\r
4812     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
4813     sizeX = newSizeX;\r
4814     sizeY = newSizeY;\r
4815     break;\r
4816 \r
4817   case WM_GETMINMAXINFO:\r
4818     /* Prevent resizing window too small */\r
4819     mmi = (MINMAXINFO *) lParam;\r
4820     mmi->ptMinTrackSize.x = 100;\r
4821     mmi->ptMinTrackSize.y = 100;\r
4822     break;\r
4823   }\r
4824   return FALSE;\r
4825 }\r
4826 \r
4827 VOID\r
4828 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
4829 {\r
4830   FARPROC lpProc;\r
4831   char *p, *q;\r
4832 \r
4833   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
4834 \r
4835   if (str == NULL) str = "";\r
4836   p = (char *) malloc(2 * strlen(str) + 2);\r
4837   q = p;\r
4838   while (*str) {\r
4839     if (*str == '\n') *q++ = '\r';\r
4840     *q++ = *str++;\r
4841   }\r
4842   *q = NULLCHAR;\r
4843   if (commentText != NULL) free(commentText);\r
4844 \r
4845   commentIndex = index;\r
4846   commentTitle = title;\r
4847   commentText = p;\r
4848   editComment = edit;\r
4849 \r
4850   if (commentDialog) {\r
4851     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
4852     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
4853   } else {\r
4854     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
4855     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
4856                  hwndMain, (DLGPROC)lpProc);\r
4857     FreeProcInstance(lpProc);\r
4858   }\r
4859   commentDialogUp = TRUE;\r
4860 }\r
4861 \r
4862 \r
4863 /*---------------------------------------------------------------------------*\\r
4864  *\r
4865  * Type-in move dialog functions\r
4866  * \r
4867 \*---------------------------------------------------------------------------*/\r
4868 \r
4869 LRESULT CALLBACK\r
4870 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4871 {\r
4872   char move[MSG_SIZ];\r
4873   HWND hInput;\r
4874   ChessMove moveType;\r
4875   int fromX, fromY, toX, toY;\r
4876   char promoChar;\r
4877 \r
4878   switch (message) {\r
4879   case WM_INITDIALOG:\r
4880     move[0] = (char) lParam;\r
4881     move[1] = NULLCHAR;\r
4882     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
4883     hInput = GetDlgItem(hDlg, OPT_Move);\r
4884     SetWindowText(hInput, move);\r
4885     SetFocus(hInput);\r
4886     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
4887     return FALSE;\r
4888 \r
4889   case WM_COMMAND:\r
4890     switch (LOWORD(wParam)) {\r
4891     case IDOK:\r
4892       if (gameMode != EditGame && currentMove != forwardMostMove && \r
4893         gameMode != Training) {\r
4894         DisplayMoveError("Displayed move is not current");\r
4895       } else {\r
4896         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
4897         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
4898           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
4899           if (gameMode != Training)\r
4900               forwardMostMove = currentMove;\r
4901           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
4902         } else {\r
4903           DisplayMoveError("Could not parse move");\r
4904         }\r
4905       }\r
4906       EndDialog(hDlg, TRUE);\r
4907       return TRUE;\r
4908     case IDCANCEL:\r
4909       EndDialog(hDlg, FALSE);\r
4910       return TRUE;\r
4911     default:\r
4912       break;\r
4913     }\r
4914     break;\r
4915   }\r
4916   return FALSE;\r
4917 }\r
4918 \r
4919 VOID\r
4920 PopUpMoveDialog(char firstchar)\r
4921 {\r
4922     FARPROC lpProc;\r
4923     \r
4924     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
4925         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
4926         gameMode == AnalyzeMode || gameMode == EditGame || \r
4927         gameMode == EditPosition || gameMode == IcsExamining ||\r
4928         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
4929         gameMode == Training) {\r
4930       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
4931       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
4932         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
4933       FreeProcInstance(lpProc);\r
4934     }\r
4935 }\r
4936 \r
4937 /*---------------------------------------------------------------------------*\\r
4938  *\r
4939  *  Error dialogs\r
4940  * \r
4941 \*---------------------------------------------------------------------------*/\r
4942 \r
4943 /* Nonmodal error box */\r
4944 VOID\r
4945 ErrorPopDown()\r
4946 {\r
4947   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
4948   if (errorDialog == NULL) return;\r
4949   DestroyWindow(errorDialog);\r
4950   errorDialog = NULL;\r
4951 }\r
4952 \r
4953 LRESULT CALLBACK\r
4954 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
4955 {\r
4956   HANDLE hwndText;\r
4957   RECT rChild;\r
4958 \r
4959   switch (message) {\r
4960   case WM_INITDIALOG:\r
4961     GetWindowRect(hDlg, &rChild);\r
4962     SetWindowPos(hDlg, NULL, rChild.left,\r
4963       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
4964       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
4965     errorDialog = hDlg;\r
4966     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
4967     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
4968     return FALSE;\r
4969 \r
4970   case WM_COMMAND:\r
4971     switch (LOWORD(wParam)) {\r
4972     case IDOK:\r
4973     case IDCANCEL:\r
4974       if (errorDialog = hDlg) errorDialog = NULL;\r
4975       DestroyWindow(hDlg);\r
4976       return TRUE;\r
4977 \r
4978     default:\r
4979       break;\r
4980     }\r
4981     break;\r
4982   }\r
4983   return FALSE;\r
4984 }\r
4985 \r
4986 /*---------------------------------------------------------------------------*\\r
4987  *\r
4988  *  Ics Interaction console functions\r
4989  *\r
4990 \*---------------------------------------------------------------------------*/\r
4991 \r
4992 #define HISTORY_SIZE 64\r
4993 static char *history[HISTORY_SIZE];\r
4994 int histIn = 0, histP = 0;\r
4995 \r
4996 VOID\r
4997 SaveInHistory(char *cmd)\r
4998 {\r
4999   if (history[histIn] != NULL) {\r
5000     free(history[histIn]);\r
5001     history[histIn] = NULL;\r
5002   }\r
5003   if (*cmd == NULLCHAR) return;\r
5004   history[histIn] = StrSave(cmd);\r
5005   histIn = (histIn + 1) % HISTORY_SIZE;\r
5006   if (history[histIn] != NULL) {\r
5007     free(history[histIn]);\r
5008     history[histIn] = NULL;\r
5009   }\r
5010   histP = histIn;\r
5011 }\r
5012 \r
5013 char *\r
5014 PrevInHistory(char *cmd)\r
5015 {\r
5016   int newhp;\r
5017   if (histP == histIn) {\r
5018     if (history[histIn] != NULL) free(history[histIn]);\r
5019     history[histIn] = StrSave(cmd);\r
5020   }\r
5021   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
5022   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
5023   histP = newhp;\r
5024   return history[histP];\r
5025 }\r
5026 \r
5027 char *\r
5028 NextInHistory()\r
5029 {\r
5030   if (histP == histIn) return NULL;\r
5031   histP = (histP + 1) % HISTORY_SIZE;\r
5032   return history[histP];\r
5033 }\r
5034 \r
5035 typedef struct {\r
5036   char *item;\r
5037   char *command;\r
5038   BOOLEAN getname;\r
5039   BOOLEAN immediate;\r
5040 } IcsTextMenuEntry;\r
5041 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
5042 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
5043 \r
5044 void\r
5045 ParseIcsTextMenu(char *icsTextMenuString)\r
5046 {\r
5047   int flags = 0;\r
5048   IcsTextMenuEntry *e = icsTextMenuEntry;\r
5049   char *p = icsTextMenuString;\r
5050   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
5051     free(e->item);\r
5052     e->item = NULL;\r
5053     if (e->command != NULL) {\r
5054       free(e->command);\r
5055       e->command = NULL;\r
5056     }\r
5057     e++;\r
5058   }\r
5059   e = icsTextMenuEntry;\r
5060   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
5061     if (*p == ';' || *p == '\n') {\r
5062       e->item = strdup("-");\r
5063       e->command = NULL;\r
5064       p++;\r
5065     } else if (*p == '-') {\r
5066       e->item = strdup("-");\r
5067       e->command = NULL;\r
5068       p++;\r
5069       if (*p) p++;\r
5070     } else {\r
5071       char *q, *r, *s, *t;\r
5072       char c;\r
5073       q = strchr(p, ',');\r
5074       if (q == NULL) break;\r
5075       *q = NULLCHAR;\r
5076       r = strchr(q + 1, ',');\r
5077       if (r == NULL) break;\r
5078       *r = NULLCHAR;\r
5079       s = strchr(r + 1, ',');\r
5080       if (s == NULL) break;\r
5081       *s = NULLCHAR;\r
5082       c = ';';\r
5083       t = strchr(s + 1, c);\r
5084       if (t == NULL) {\r
5085         c = '\n';\r
5086         t = strchr(s + 1, c);\r
5087       }\r
5088       if (t != NULL) *t = NULLCHAR;\r
5089       e->item = strdup(p);\r
5090       e->command = strdup(q + 1);\r
5091       e->getname = *(r + 1) != '0';\r
5092       e->immediate = *(s + 1) != '0';\r
5093       *q = ',';\r
5094       *r = ',';\r
5095       *s = ',';\r
5096       if (t == NULL) break;\r
5097       *t = c;\r
5098       p = t + 1;\r
5099     }\r
5100     e++;\r
5101   } \r
5102 }\r
5103 \r
5104 HMENU\r
5105 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
5106 {\r
5107   HMENU hmenu, h;\r
5108   int i = 0;\r
5109   hmenu = LoadMenu(hInst, "TextMenu");\r
5110   h = GetSubMenu(hmenu, 0);\r
5111   while (e->item) {\r
5112     if (strcmp(e->item, "-") == 0) {\r
5113       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
5114     } else {\r
5115       if (e->item[0] == '|') {\r
5116         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
5117                    IDM_CommandX + i, &e->item[1]);\r
5118       } else {\r
5119         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
5120       }\r
5121     }\r
5122     e++;\r
5123     i++;\r
5124   } \r
5125   return hmenu;\r
5126 }\r
5127 \r
5128 WNDPROC consoleTextWindowProc;\r
5129 \r
5130 void\r
5131 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
5132 {\r
5133   char buf[MSG_SIZ], name[MSG_SIZ];\r
5134   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5135   CHARRANGE sel;\r
5136 \r
5137   if (!getname) {\r
5138     SetWindowText(hInput, command);\r
5139     if (immediate) {\r
5140       SendMessage(hInput, WM_CHAR, '\r', 0);\r
5141     } else {\r
5142       sel.cpMin = 999999;\r
5143       sel.cpMax = 999999;\r
5144       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5145       SetFocus(hInput);\r
5146     }\r
5147     return;\r
5148   }    \r
5149   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5150   if (sel.cpMin == sel.cpMax) {\r
5151     /* Expand to surrounding word */\r
5152     TEXTRANGE tr;\r
5153     do {\r
5154       tr.chrg.cpMax = sel.cpMin;\r
5155       tr.chrg.cpMin = --sel.cpMin;\r
5156       if (sel.cpMin < 0) break;\r
5157       tr.lpstrText = name;\r
5158       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
5159     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
5160     sel.cpMin++;\r
5161 \r
5162     do {\r
5163       tr.chrg.cpMin = sel.cpMax;\r
5164       tr.chrg.cpMax = ++sel.cpMax;\r
5165       tr.lpstrText = name;\r
5166       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
5167     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
5168     sel.cpMax--;\r
5169 \r
5170     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
5171       MessageBeep(MB_ICONEXCLAMATION);\r
5172       return;\r
5173     }\r
5174     tr.chrg = sel;\r
5175     tr.lpstrText = name;\r
5176     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
5177   } else {\r
5178     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
5179       MessageBeep(MB_ICONEXCLAMATION);\r
5180       return;\r
5181     }\r
5182     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
5183   }\r
5184   if (immediate) {\r
5185     sprintf(buf, "%s %s", command, name);\r
5186     SetWindowText(hInput, buf);\r
5187     SendMessage(hInput, WM_CHAR, '\r', 0);\r
5188   } else {\r
5189     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
5190     SetWindowText(hInput, buf);\r
5191     sel.cpMin = 999999;\r
5192     sel.cpMax = 999999;\r
5193     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5194     SetFocus(hInput);\r
5195   }\r
5196 }\r
5197 \r
5198 LRESULT CALLBACK \r
5199 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5200 {\r
5201   HWND hInput;\r
5202   CHARRANGE sel;\r
5203 \r
5204   switch (message) {\r
5205   case WM_KEYDOWN:\r
5206     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
5207     switch (wParam) {\r
5208     case VK_PRIOR:\r
5209       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
5210       return 0;\r
5211     case VK_NEXT:\r
5212       sel.cpMin = 999999;\r
5213       sel.cpMax = 999999;\r
5214       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5215       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
5216       return 0;\r
5217     }\r
5218     break;\r
5219   case WM_CHAR:\r
5220     if (wParam == '\t') {\r
5221       if (GetKeyState(VK_SHIFT) < 0) {\r
5222         /* shifted */\r
5223         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
5224         if (buttonDesc[0].hwnd) {\r
5225           SetFocus(buttonDesc[0].hwnd);\r
5226         } else {\r
5227           SetFocus(hwndMain);\r
5228         }\r
5229       } else {\r
5230         /* unshifted */\r
5231         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
5232       }\r
5233     } else {\r
5234       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5235       SetFocus(hInput);\r
5236       SendMessage(hInput, message, wParam, lParam);\r
5237     }\r
5238     return 0;\r
5239   case WM_PASTE:\r
5240     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5241     SetFocus(hInput);\r
5242     return SendMessage(hInput, message, wParam, lParam);\r
5243   case WM_MBUTTONDOWN:\r
5244     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
5245   case WM_RBUTTONDOWN:\r
5246     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
5247       /* Move selection here if it was empty */\r
5248       POINT pt;\r
5249       pt.x = LOWORD(lParam);\r
5250       pt.y = HIWORD(lParam);\r
5251       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5252       if (sel.cpMin == sel.cpMax) {\r
5253         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
5254         sel.cpMax = sel.cpMin;\r
5255         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5256       }\r
5257       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
5258     }\r
5259     return 0;\r
5260   case WM_RBUTTONUP:\r
5261     if (GetKeyState(VK_SHIFT) & ~1) {\r
5262       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
5263         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
5264     } else {\r
5265       POINT pt;\r
5266       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
5267       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5268       if (sel.cpMin == sel.cpMax) {\r
5269         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
5270         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
5271       }\r
5272       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
5273         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
5274       }\r
5275       pt.x = LOWORD(lParam);\r
5276       pt.y = HIWORD(lParam);\r
5277       MenuPopup(hwnd, pt, hmenu, -1);\r
5278     }\r
5279     return 0;\r
5280   case WM_COMMAND:\r
5281     switch (LOWORD(wParam)) {\r
5282     case IDM_QuickPaste:\r
5283       {\r
5284         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5285         if (sel.cpMin == sel.cpMax) {\r
5286           MessageBeep(MB_ICONEXCLAMATION);\r
5287           return 0;\r
5288         }\r
5289         SendMessage(hwnd, WM_COPY, 0, 0);\r
5290         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5291         SendMessage(hInput, WM_PASTE, 0, 0);\r
5292         SetFocus(hInput);\r
5293         return 0;\r
5294       }\r
5295     case IDM_Cut:\r
5296       SendMessage(hwnd, WM_CUT, 0, 0);\r
5297       return 0;\r
5298     case IDM_Paste:\r
5299       SendMessage(hwnd, WM_PASTE, 0, 0);\r
5300       return 0;\r
5301     case IDM_Copy:\r
5302       SendMessage(hwnd, WM_COPY, 0, 0);\r
5303       return 0;\r
5304     default:\r
5305       {\r
5306         int i = LOWORD(wParam) - IDM_CommandX;\r
5307         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
5308             icsTextMenuEntry[i].command != NULL) {\r
5309           CommandX(hwnd, icsTextMenuEntry[i].command,\r
5310                    icsTextMenuEntry[i].getname,\r
5311                    icsTextMenuEntry[i].immediate);\r
5312           return 0;\r
5313         }\r
5314       }\r
5315       break;\r
5316     }\r
5317     break;\r
5318   }\r
5319   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
5320 }\r
5321 \r
5322 WNDPROC consoleInputWindowProc;\r
5323 \r
5324 LRESULT CALLBACK\r
5325 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5326 {\r
5327   char buf[MSG_SIZ];\r
5328   char *p;\r
5329   static BOOL sendNextChar = FALSE;\r
5330   static BOOL quoteNextChar = FALSE;\r
5331   InputSource *is = consoleInputSource;\r
5332   CHARFORMAT cf;\r
5333   CHARRANGE sel;\r
5334 \r
5335   switch (message) {\r
5336   case WM_CHAR:\r
5337     if (!appData.localLineEditing || sendNextChar) {\r
5338       is->buf[0] = (CHAR) wParam;\r
5339       is->count = 1;\r
5340       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
5341       sendNextChar = FALSE;\r
5342       return 0;\r
5343     }\r
5344     if (quoteNextChar) {\r
5345       buf[0] = (char) wParam;\r
5346       buf[1] = NULLCHAR;\r
5347       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
5348       quoteNextChar = FALSE;\r
5349       return 0;\r
5350     }\r
5351     switch (wParam) {\r
5352     case '\r':   /* Enter key */\r
5353       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
5354       if (consoleEcho) SaveInHistory(is->buf);\r
5355       is->buf[is->count++] = '\n';\r
5356       is->buf[is->count] = NULLCHAR;\r
5357       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
5358       if (consoleEcho) {\r
5359         ConsoleOutput(is->buf, is->count, TRUE);\r
5360       } else if (appData.localLineEditing) {\r
5361         ConsoleOutput("\n", 1, TRUE);\r
5362       }\r
5363       /* fall thru */\r
5364     case '\033': /* Escape key */\r
5365       SetWindowText(hwnd, "");\r
5366       cf.cbSize = sizeof(CHARFORMAT);\r
5367       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
5368       if (consoleEcho) {\r
5369         cf.crTextColor = textAttribs[ColorNormal].color;\r
5370       } else {\r
5371         cf.crTextColor = COLOR_ECHOOFF;\r
5372       }\r
5373       cf.dwEffects = textAttribs[ColorNormal].effects;\r
5374       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
5375       return 0;\r
5376     case '\t':   /* Tab key */\r
5377       if (GetKeyState(VK_SHIFT) < 0) {\r
5378         /* shifted */\r
5379         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
5380       } else {\r
5381         /* unshifted */\r
5382         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
5383         if (buttonDesc[0].hwnd) {\r
5384           SetFocus(buttonDesc[0].hwnd);\r
5385         } else {\r
5386           SetFocus(hwndMain);\r
5387         }\r
5388       }\r
5389       return 0;\r
5390     case '\023': /* Ctrl+S */\r
5391       sendNextChar = TRUE;\r
5392       return 0;\r
5393     case '\021': /* Ctrl+Q */\r
5394       quoteNextChar = TRUE;\r
5395       return 0;\r
5396     default:\r
5397       break;\r
5398     }\r
5399     break;\r
5400   case WM_KEYDOWN:\r
5401     switch (wParam) {\r
5402     case VK_UP:\r
5403       GetWindowText(hwnd, buf, MSG_SIZ);\r
5404       p = PrevInHistory(buf);\r
5405       if (p != NULL) {\r
5406         SetWindowText(hwnd, p);\r
5407         sel.cpMin = 999999;\r
5408         sel.cpMax = 999999;\r
5409         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5410         return 0;\r
5411       }\r
5412       break;\r
5413     case VK_DOWN:\r
5414       p = NextInHistory();\r
5415       if (p != NULL) {\r
5416         SetWindowText(hwnd, p);\r
5417         sel.cpMin = 999999;\r
5418         sel.cpMax = 999999;\r
5419         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5420         return 0;\r
5421       }\r
5422       break;\r
5423     case VK_HOME:\r
5424     case VK_END:\r
5425       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
5426       /* fall thru */\r
5427     case VK_PRIOR:\r
5428     case VK_NEXT:\r
5429       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
5430       return 0;\r
5431     }\r
5432     break;\r
5433   case WM_MBUTTONDOWN:\r
5434     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
5435       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
5436     break;\r
5437   case WM_RBUTTONUP:\r
5438     if (GetKeyState(VK_SHIFT) & ~1) {\r
5439       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
5440         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
5441     } else {\r
5442       POINT pt;\r
5443       HMENU hmenu;\r
5444       hmenu = LoadMenu(hInst, "InputMenu");\r
5445       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5446       if (sel.cpMin == sel.cpMax) {\r
5447         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
5448         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
5449       }\r
5450       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
5451         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
5452       }\r
5453       pt.x = LOWORD(lParam);\r
5454       pt.y = HIWORD(lParam);\r
5455       MenuPopup(hwnd, pt, hmenu, -1);\r
5456     }\r
5457     return 0;\r
5458   case WM_COMMAND:\r
5459     switch (LOWORD(wParam)) { \r
5460     case IDM_Undo:\r
5461       SendMessage(hwnd, EM_UNDO, 0, 0);\r
5462       return 0;\r
5463     case IDM_SelectAll:\r
5464       sel.cpMin = 0;\r
5465       sel.cpMax = -1; /*999999?*/\r
5466       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5467       return 0;\r
5468     case IDM_Cut:\r
5469       SendMessage(hwnd, WM_CUT, 0, 0);\r
5470       return 0;\r
5471     case IDM_Paste:\r
5472       SendMessage(hwnd, WM_PASTE, 0, 0);\r
5473       return 0;\r
5474     case IDM_Copy:\r
5475       SendMessage(hwnd, WM_COPY, 0, 0);\r
5476       return 0;\r
5477     }\r
5478     break;\r
5479   }\r
5480   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
5481 }\r
5482 \r
5483 #define CO_MAX  100000\r
5484 #define CO_TRIM   1000\r
5485 \r
5486 LRESULT CALLBACK\r
5487 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5488 {\r
5489   static HWND hText, hInput, hFocus;\r
5490   InputSource *is = consoleInputSource;\r
5491   RECT rect;\r
5492   static int sizeX, sizeY;\r
5493   int newSizeX, newSizeY;\r
5494   MINMAXINFO *mmi;\r
5495 \r
5496   switch (message) {\r
5497   case WM_INITDIALOG: /* message: initialize dialog box */\r
5498     hwndConsole = hDlg;\r
5499     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
5500     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
5501     SetFocus(hInput);\r
5502     consoleTextWindowProc = (WNDPROC)\r
5503       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
5504     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
5505     consoleInputWindowProc = (WNDPROC)\r
5506       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
5507     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
5508     Colorize(ColorNormal, TRUE);\r
5509     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
5510     ChangedConsoleFont();\r
5511     GetClientRect(hDlg, &rect);\r
5512     sizeX = rect.right;\r
5513     sizeY = rect.bottom;\r
5514     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&\r
5515         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {\r
5516       WINDOWPLACEMENT wp;\r
5517       EnsureOnScreen(&consoleX, &consoleY);\r
5518       wp.length = sizeof(WINDOWPLACEMENT);\r
5519       wp.flags = 0;\r
5520       wp.showCmd = SW_SHOW;\r
5521       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
5522       wp.rcNormalPosition.left = consoleX;\r
5523       wp.rcNormalPosition.right = consoleX + consoleW;\r
5524       wp.rcNormalPosition.top = consoleY;\r
5525       wp.rcNormalPosition.bottom = consoleY + consoleH;\r
5526       SetWindowPlacement(hDlg, &wp);\r
5527     }\r
5528     return FALSE;\r
5529 \r
5530   case WM_SETFOCUS:\r
5531     SetFocus(hInput);\r
5532     return 0;\r
5533 \r
5534   case WM_CLOSE:\r
5535     ExitEvent(0);\r
5536     /* not reached */\r
5537     break;\r
5538 \r
5539   case WM_SIZE:\r
5540     if (IsIconic(hDlg)) break;\r
5541     newSizeX = LOWORD(lParam);\r
5542     newSizeY = HIWORD(lParam);\r
5543     if (sizeX != newSizeX || sizeY != newSizeY) {\r
5544       RECT rectText, rectInput;\r
5545       POINT pt;\r
5546       int newTextHeight, newTextWidth;\r
5547       GetWindowRect(hText, &rectText);\r
5548       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
5549       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
5550       if (newTextHeight < 0) {\r
5551         newSizeY += -newTextHeight;\r
5552         newTextHeight = 0;\r
5553       }\r
5554       SetWindowPos(hText, NULL, 0, 0,\r
5555         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
5556       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
5557       pt.x = rectInput.left;\r
5558       pt.y = rectInput.top + newSizeY - sizeY;\r
5559       ScreenToClient(hDlg, &pt);\r
5560       SetWindowPos(hInput, NULL, \r
5561         pt.x, pt.y, /* needs client coords */   \r
5562         rectInput.right - rectInput.left + newSizeX - sizeX,\r
5563         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
5564     }\r
5565     sizeX = newSizeX;\r
5566     sizeY = newSizeY;\r
5567     break;\r
5568 \r
5569   case WM_GETMINMAXINFO:\r
5570     /* Prevent resizing window too small */\r
5571     mmi = (MINMAXINFO *) lParam;\r
5572     mmi->ptMinTrackSize.x = 100;\r
5573     mmi->ptMinTrackSize.y = 100;\r
5574     break;\r
5575   }\r
5576   return DefWindowProc(hDlg, message, wParam, lParam);\r
5577 }\r
5578 \r
5579 \r
5580 VOID\r
5581 ConsoleCreate()\r
5582 {\r
5583   HWND hCons;\r
5584   if (hwndConsole) return;\r
5585   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
5586   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
5587 }\r
5588 \r
5589 \r
5590 VOID\r
5591 ConsoleOutput(char* data, int length, int forceVisible)\r
5592 {\r
5593   HWND hText;\r
5594   int trim, exlen;\r
5595   char *p, *q;\r
5596   char buf[CO_MAX+1];\r
5597   POINT pEnd;\r
5598   RECT rect;\r
5599   static int delayLF = 0;\r
5600   CHARRANGE savesel, sel;\r
5601 \r
5602   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
5603   p = data;\r
5604   q = buf;\r
5605   if (delayLF) {\r
5606     *q++ = '\r';\r
5607     *q++ = '\n';\r
5608     delayLF = 0;\r
5609   }\r
5610   while (length--) {\r
5611     if (*p == '\n') {\r
5612       if (*++p) {\r
5613         *q++ = '\r';\r
5614         *q++ = '\n';\r
5615       } else {\r
5616         delayLF = 1;\r
5617       }\r
5618     } else if (*p == '\007') {\r
5619        MyPlaySound(&sounds[(int)SoundBell]);\r
5620        p++;\r
5621     } else {\r
5622       *q++ = *p++;\r
5623     }\r
5624   }\r
5625   *q = NULLCHAR;\r
5626   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5627   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
5628   /* Save current selection */\r
5629   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
5630   exlen = GetWindowTextLength(hText);\r
5631   /* Find out whether current end of text is visible */\r
5632   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
5633   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
5634   /* Trim existing text if it's too long */\r
5635   if (exlen + (q - buf) > CO_MAX) {\r
5636     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
5637     sel.cpMin = 0;\r
5638     sel.cpMax = trim;\r
5639     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5640     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
5641     exlen -= trim;\r
5642     savesel.cpMin -= trim;\r
5643     savesel.cpMax -= trim;\r
5644     if (exlen < 0) exlen = 0;\r
5645     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
5646     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
5647   }\r
5648   /* Append the new text */\r
5649   sel.cpMin = exlen;\r
5650   sel.cpMax = exlen;\r
5651   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5652   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
5653   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
5654   if (forceVisible || exlen == 0 ||\r
5655       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
5656        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
5657     /* Scroll to make new end of text visible if old end of text\r
5658        was visible or new text is an echo of user typein */\r
5659     sel.cpMin = 9999999;\r
5660     sel.cpMax = 9999999;\r
5661     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5662     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
5663     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
5664     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
5665   }\r
5666   if (savesel.cpMax == exlen || forceVisible) {\r
5667     /* Move insert point to new end of text if it was at the old\r
5668        end of text or if the new text is an echo of user typein */\r
5669     sel.cpMin = 9999999;\r
5670     sel.cpMax = 9999999;\r
5671     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5672   } else {\r
5673     /* Restore previous selection */\r
5674     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
5675   }\r
5676   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
5677 }\r
5678 \r
5679 /*---------*/\r
5680 \r
5681 \r
5682 void\r
5683 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
5684               RECT *rect, char *color)\r
5685 {\r
5686   char buf[100];\r
5687   char *str;\r
5688   COLORREF oldFg, oldBg;\r
5689   HFONT oldFont;\r
5690 \r
5691   if (appData.clockMode) {\r
5692     if (tinyLayout)\r
5693       sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));\r
5694     else\r
5695       sprintf(buf, "%s: %s", color, TimeString(timeRemaining));\r
5696     str = buf;\r
5697   } else {\r
5698     str = color;\r
5699   }\r
5700 \r
5701   if (highlight) {\r
5702     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
5703     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
5704   } else {\r
5705     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
5706     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
5707   }\r
5708   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
5709 \r
5710   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
5711              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
5712              rect, str, strlen(str), NULL);\r
5713 \r
5714   (void) SetTextColor(hdc, oldFg);\r
5715   (void) SetBkColor(hdc, oldBg);\r
5716   (void) SelectObject(hdc, oldFont);\r
5717 }\r
5718 \r
5719 \r
5720 int\r
5721 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
5722            OVERLAPPED *ovl)\r
5723 {\r
5724   int ok, err;\r
5725 \r
5726   ResetEvent(ovl->hEvent);\r
5727   ovl->Offset = ovl->OffsetHigh = 0;\r
5728   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
5729   if (ok) {\r
5730     err = NO_ERROR;\r
5731   } else {\r
5732     err = GetLastError();\r
5733     if (err == ERROR_IO_PENDING) {\r
5734       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
5735       if (ok)\r
5736         err = NO_ERROR;\r
5737       else\r
5738         err = GetLastError();\r
5739     }\r
5740   }\r
5741   return err;\r
5742 }\r
5743 \r
5744 int\r
5745 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
5746             OVERLAPPED *ovl)\r
5747 {\r
5748   int ok, err;\r
5749 \r
5750   ResetEvent(ovl->hEvent);\r
5751   ovl->Offset = ovl->OffsetHigh = 0;\r
5752   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
5753   if (ok) {\r
5754     err = NO_ERROR;\r
5755   } else {\r
5756     err = GetLastError();\r
5757     if (err == ERROR_IO_PENDING) {\r
5758       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
5759       if (ok)\r
5760         err = NO_ERROR;\r
5761       else\r
5762         err = GetLastError();\r
5763     }\r
5764   }\r
5765   return err;\r
5766 }\r
5767 \r
5768 \r
5769 DWORD\r
5770 InputThread(LPVOID arg)\r
5771 {\r
5772   InputSource *is;\r
5773   OVERLAPPED ovl;\r
5774 \r
5775   is = (InputSource *) arg;\r
5776   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
5777   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
5778   while (is->hThread != NULL) {\r
5779     is->error = DoReadFile(is->hFile, is->next,\r
5780                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
5781                            &is->count, &ovl);\r
5782     if (is->error == NO_ERROR) {\r
5783       is->next += is->count;\r
5784     } else {\r
5785       if (is->error == ERROR_BROKEN_PIPE) {\r
5786         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
5787         is->count = 0;\r
5788       } else {\r
5789         is->count = (DWORD) -1;\r
5790       }\r
5791     }\r
5792     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
5793     if (is->count <= 0) break;  /* Quit on EOF or error */\r
5794   }\r
5795   CloseHandle(ovl.hEvent);\r
5796   CloseHandle(is->hFile);\r
5797   return 0;\r
5798 }\r
5799 \r
5800 \r
5801 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
5802 DWORD\r
5803 NonOvlInputThread(LPVOID arg)\r
5804 {\r
5805   InputSource *is;\r
5806   char *p, *q;\r
5807   int i;\r
5808   char prev;\r
5809 \r
5810   is = (InputSource *) arg;\r
5811   while (is->hThread != NULL) {\r
5812     is->error = ReadFile(is->hFile, is->next,\r
5813                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
5814                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
5815     if (is->error == NO_ERROR) {\r
5816       /* Change CRLF to LF */\r
5817       if (is->next > is->buf) {\r
5818         p = is->next - 1;\r
5819         i = is->count + 1;\r
5820       } else {\r
5821         p = is->next;\r
5822         i = is->count;\r
5823       }\r
5824       q = p;\r
5825       prev = NULLCHAR;\r
5826       while (i > 0) {\r
5827         if (prev == '\r' && *p == '\n') {\r
5828           *(q-1) = '\n';\r
5829           is->count--;\r
5830         } else { \r
5831           *q++ = *p;\r
5832         }\r
5833         prev = *p++;\r
5834         i--;\r
5835       }\r
5836       *q = NULLCHAR;\r
5837       is->next = q;\r
5838     } else {\r
5839       if (is->error == ERROR_BROKEN_PIPE) {\r
5840         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
5841         is->count = 0; \r
5842       } else {\r
5843         is->count = (DWORD) -1;\r
5844       }\r
5845     }\r
5846     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
5847     if (is->count < 0) break;  /* Quit on error */\r
5848   }\r
5849   CloseHandle(is->hFile);\r
5850   return 0;\r
5851 }\r
5852 \r
5853 DWORD\r
5854 SocketInputThread(LPVOID arg)\r
5855 {\r
5856   InputSource *is;\r
5857 \r
5858   is = (InputSource *) arg;\r
5859   while (is->hThread != NULL) {\r
5860     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
5861     if ((int)is->count == SOCKET_ERROR) {\r
5862       is->count = (DWORD) -1;\r
5863       is->error = WSAGetLastError();\r
5864     } else {\r
5865       is->error = NO_ERROR;\r
5866       is->next += is->count;\r
5867       if (is->count == 0 && is->second == is) {\r
5868         /* End of file on stderr; quit with no message */\r
5869         break;\r
5870       }\r
5871     }\r
5872     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
5873     if (is->count <= 0) break;  /* Quit on EOF or error */\r
5874   }\r
5875   return 0;\r
5876 }\r
5877 \r
5878 VOID\r
5879 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5880 {\r
5881   InputSource *is;\r
5882 \r
5883   is = (InputSource *) lParam;\r
5884   if (is->lineByLine) {\r
5885     /* Feed in lines one by one */\r
5886     char *p = is->buf;\r
5887     char *q = p;\r
5888     while (q < is->next) {\r
5889       if (*q++ == '\n') {\r
5890         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
5891         p = q;\r
5892       }\r
5893     }\r
5894     /* Move any partial line to the start of the buffer */\r
5895     q = is->buf;\r
5896     while (p < is->next) {\r
5897       *q++ = *p++;\r
5898     }\r
5899     is->next = q;\r
5900     if (is->error != NO_ERROR || is->count == 0) {\r
5901       /* Notify backend of the error.  Note: If there was a partial\r
5902          line at the end, it is not flushed through. */\r
5903       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
5904     }\r
5905   } else {\r
5906     /* Feed in the whole chunk of input at once */\r
5907     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
5908     is->next = is->buf;\r
5909   }\r
5910 }\r
5911 \r
5912 /*---------------------------------------------------------------------------*\\r
5913  *\r
5914  *  Menu enables. Used when setting various modes.\r
5915  *\r
5916 \*---------------------------------------------------------------------------*/\r
5917 \r
5918 typedef struct {\r
5919   int item;\r
5920   int flags;\r
5921 } Enables;\r
5922 \r
5923 VOID\r
5924 SetMenuEnables(HMENU hmenu, Enables *enab)\r
5925 {\r
5926   while (enab->item > 0) {\r
5927     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
5928     enab++;\r
5929   }\r
5930 }\r
5931 \r
5932 Enables gnuEnables[] = {\r
5933   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
5934   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
5935   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
5936   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
5937   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
5938   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
5939   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
5940   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
5941   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
5942   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
5943   { -1, -1 }\r
5944 };\r
5945 \r
5946 Enables icsEnables[] = {\r
5947   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
5948   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
5949   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
5950   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
5951   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
5952   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
5953   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
5954   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
5955   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
5956   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
5957   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
5958   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
5959   { -1, -1 }\r
5960 };\r
5961 \r
5962 #ifdef ZIPPY\r
5963 Enables zippyEnables[] = {\r
5964   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
5965   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
5966   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
5967   { -1, -1 }\r
5968 };\r
5969 #endif\r
5970 \r
5971 Enables ncpEnables[] = {\r
5972   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
5973   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
5974   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
5975   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
5976   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
5977   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
5978   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
5979   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
5980   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
5981   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
5982   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
5983   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
5984   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
5985   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
5986   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
5987   { -1, -1 }\r
5988 };\r
5989 \r
5990 Enables trainingOnEnables[] = {\r
5991   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
5992   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
5993   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
5994   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
5995   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
5996   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
5997   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
5998   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
5999   { -1, -1 }\r
6000 };\r
6001 \r
6002 Enables trainingOffEnables[] = {\r
6003   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
6004   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
6005   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
6006   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
6007   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
6008   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
6009   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
6010   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
6011   { -1, -1 }\r
6012 };\r
6013 \r
6014 /* These modify either ncpEnables or gnuEnables */\r
6015 Enables cmailEnables[] = {\r
6016   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
6017   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
6018   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
6019   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
6020   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
6021   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
6022   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
6023   { -1, -1 }\r
6024 };\r
6025 \r
6026 Enables machineThinkingEnables[] = {\r
6027   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
6028   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
6029   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
6030   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
6031   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
6032   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
6033   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
6034   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
6035   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
6036   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
6037   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
6038   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
6039   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
6040   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
6041   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
6042   { -1, -1 }\r
6043 };\r
6044 \r
6045 Enables userThinkingEnables[] = {\r
6046   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
6047   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
6048   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
6049   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
6050   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
6051   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
6052   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
6053   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
6054   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
6055   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
6056   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
6057   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
6058   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
6059   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
6060   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
6061   { -1, -1 }\r
6062 };\r
6063 \r
6064 /*---------------------------------------------------------------------------*\\r
6065  *\r
6066  *  Front-end interface functions exported by XBoard.\r
6067  *  Functions appear in same order as prototypes in frontend.h.\r
6068  * \r
6069 \*---------------------------------------------------------------------------*/\r
6070 VOID\r
6071 ModeHighlight()\r
6072 {\r
6073   static UINT prevChecked = 0;\r
6074   static int prevPausing = 0;\r
6075   UINT nowChecked;\r
6076 \r
6077   if (pausing != prevPausing) {\r
6078     prevPausing = pausing;\r
6079     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
6080                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
6081     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
6082   }\r
6083 \r
6084   switch (gameMode) {\r
6085   case BeginningOfGame:\r
6086     if (appData.icsActive)\r
6087       nowChecked = IDM_IcsClient;\r
6088     else if (appData.noChessProgram)\r
6089       nowChecked = IDM_EditGame;\r
6090     else\r
6091       nowChecked = IDM_MachineBlack;\r
6092     break;\r
6093   case MachinePlaysBlack:\r
6094     nowChecked = IDM_MachineBlack;\r
6095     break;\r
6096   case MachinePlaysWhite:\r
6097     nowChecked = IDM_MachineWhite;\r
6098     break;\r
6099   case TwoMachinesPlay:\r
6100     nowChecked = IDM_TwoMachines;\r
6101     break;\r
6102   case AnalyzeMode:\r
6103     nowChecked = IDM_AnalysisMode;\r
6104     break;\r
6105   case AnalyzeFile:\r
6106     nowChecked = IDM_AnalyzeFile;\r
6107     break;\r
6108   case EditGame:\r
6109     nowChecked = IDM_EditGame;\r
6110     break;\r
6111   case PlayFromGameFile:\r
6112     nowChecked = IDM_LoadGame;\r
6113     break;\r
6114   case EditPosition:\r
6115     nowChecked = IDM_EditPosition;\r
6116     break;\r
6117   case Training:\r
6118     nowChecked = IDM_Training;\r
6119     break;\r
6120   case IcsPlayingWhite:\r
6121   case IcsPlayingBlack:\r
6122   case IcsObserving:\r
6123   case IcsIdle:\r
6124     nowChecked = IDM_IcsClient;\r
6125     break;\r
6126   default:\r
6127   case EndOfGame:\r
6128     nowChecked = 0;\r
6129     break;\r
6130   }\r
6131   if (prevChecked != 0)\r
6132     (void) CheckMenuItem(GetMenu(hwndMain),\r
6133                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
6134   if (nowChecked != 0)\r
6135     (void) CheckMenuItem(GetMenu(hwndMain),\r
6136                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
6137 \r
6138   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
6139     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
6140                           MF_BYCOMMAND|MF_ENABLED);\r
6141   } else {\r
6142     (void) EnableMenuItem(GetMenu(hwndMain), \r
6143                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
6144   }\r
6145 \r
6146   prevChecked = nowChecked;\r
6147 }\r
6148 \r
6149 VOID\r
6150 SetICSMode()\r
6151 {\r
6152   HMENU hmenu = GetMenu(hwndMain);\r
6153   SetMenuEnables(hmenu, icsEnables);\r
6154   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
6155     MF_BYPOSITION|MF_ENABLED);\r
6156 #ifdef ZIPPY\r
6157   if (appData.zippyPlay) {\r
6158     SetMenuEnables(hmenu, zippyEnables);\r
6159   }\r
6160 #endif\r
6161 }\r
6162 \r
6163 VOID\r
6164 SetGNUMode()\r
6165 {\r
6166   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
6167 }\r
6168 \r
6169 VOID\r
6170 SetNCPMode()\r
6171 {\r
6172   HMENU hmenu = GetMenu(hwndMain);\r
6173   SetMenuEnables(hmenu, ncpEnables);\r
6174   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
6175     MF_BYPOSITION|MF_GRAYED);\r
6176     DrawMenuBar(hwndMain);\r
6177 }\r
6178 \r
6179 VOID\r
6180 SetCmailMode()\r
6181 {\r
6182   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
6183 }\r
6184 \r
6185 VOID \r
6186 SetTrainingModeOn()\r
6187 {\r
6188   int i;\r
6189   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
6190   for (i = 0; i < N_BUTTONS; i++) {\r
6191     if (buttonDesc[i].hwnd != NULL)\r
6192       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
6193   }\r
6194   CommentPopDown();\r
6195 }\r
6196 \r
6197 VOID SetTrainingModeOff()\r
6198 {\r
6199   int i;\r
6200   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
6201   for (i = 0; i < N_BUTTONS; i++) {\r
6202     if (buttonDesc[i].hwnd != NULL)\r
6203       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
6204   }\r
6205 }\r
6206 \r
6207 \r
6208 VOID\r
6209 SetUserThinkingEnables()\r
6210 {\r
6211   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
6212 }\r
6213 \r
6214 VOID\r
6215 SetMachineThinkingEnables()\r
6216 {\r
6217   HMENU hMenu = GetMenu(hwndMain);\r
6218   int flags = MF_BYCOMMAND|MF_ENABLED;\r
6219 \r
6220   SetMenuEnables(hMenu, machineThinkingEnables);\r
6221 \r
6222   if (gameMode == MachinePlaysBlack) {\r
6223     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
6224   } else if (gameMode == MachinePlaysWhite) {\r
6225     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
6226   } else if (gameMode == TwoMachinesPlay) {\r
6227     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
6228   }\r
6229 }\r
6230 \r
6231 \r
6232 VOID\r
6233 DisplayTitle(char *str)\r
6234 {\r
6235   char title[MSG_SIZ], *host;\r
6236   if (str[0] != NULLCHAR) {\r
6237     strcpy(title, str);\r
6238   } else if (appData.icsActive) {\r
6239     if (appData.icsCommPort[0] != NULLCHAR)\r
6240       host = "ICS";\r
6241     else \r
6242       host = appData.icsHost;\r
6243     sprintf(title, "%s: %s", szTitle, host);\r
6244   } else if (appData.noChessProgram) {\r
6245     strcpy(title, szTitle);\r
6246   } else {\r
6247     strcpy(title, szTitle);\r
6248     strcat(title, ": ");\r
6249     strcat(title, first.tidy);\r
6250   }\r
6251   SetWindowText(hwndMain, title);\r
6252 }\r
6253 \r
6254 \r
6255 VOID\r
6256 DisplayMessage(char *str1, char *str2)\r
6257 {\r
6258   HDC hdc;\r
6259   HFONT oldFont;\r
6260   int remain = MESSAGE_TEXT_MAX - 1;\r
6261   int len;\r
6262 \r
6263   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
6264   messageText[0] = NULLCHAR;\r
6265   if (*str1) {\r
6266     len = strlen(str1);\r
6267     if (len > remain) len = remain;\r
6268     strncpy(messageText, str1, len);\r
6269     messageText[len] = NULLCHAR;\r
6270     remain -= len;\r
6271   }\r
6272   if (*str2 && remain >= 2) {\r
6273     if (*str1) {\r
6274       strcat(messageText, "  ");\r
6275       remain -= 2;\r
6276     }\r
6277     len = strlen(str2);\r
6278     if (len > remain) len = remain;\r
6279     strncat(messageText, str2, len);\r
6280   }\r
6281   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
6282 \r
6283   if (IsIconic(hwndMain)) return;\r
6284   hdc = GetDC(hwndMain);\r
6285   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
6286   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
6287              &messageRect, messageText, strlen(messageText), NULL);\r
6288   (void) SelectObject(hdc, oldFont);\r
6289   (void) ReleaseDC(hwndMain, hdc);\r
6290 }\r
6291 \r
6292 VOID\r
6293 DisplayError(char *str, int error)\r
6294 {\r
6295   FARPROC lpProc;\r
6296   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
6297   int len;\r
6298   char *p, *q;\r
6299 \r
6300   if (error == 0) {\r
6301     strcpy(buf, str);\r
6302   } else {\r
6303     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
6304                         NULL, error, LANG_NEUTRAL,\r
6305                         (LPSTR) buf2, MSG_SIZ, NULL);\r
6306     if (len > 0) {\r
6307       sprintf(buf, "%s:\n%s", str, buf2);\r
6308     } else {\r
6309       ErrorMap *em = errmap;\r
6310       while (em->err != 0 && em->err != error) em++;\r
6311       if (em->err != 0) {\r
6312         sprintf(buf, "%s:\n%s", str, em->msg);\r
6313       } else {\r
6314         sprintf(buf, "%s:\nError code %d", str, error);\r
6315       }\r
6316     }\r
6317   }\r
6318   p = buf;\r
6319   q = errorMessage;\r
6320   while (*p) {\r
6321     if (*p == '\n') {\r
6322       if (hwndMain != NULL /*!!?*/) {\r
6323         *q++ = '\r';\r
6324         *q++ = *p++;\r
6325       } else {\r
6326         *q++ = ' ';\r
6327         p++;\r
6328       }\r
6329     } else {\r
6330       *q++ = *p++;\r
6331     }\r
6332   }\r
6333   *q = NULLCHAR;\r
6334   \r
6335   if (hwndMain == NULL) {\r
6336     MessageBox(NULL, errorMessage, "Error", MB_OK|MB_ICONEXCLAMATION);\r
6337   } else {\r
6338     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
6339     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
6340       hwndMain, (DLGPROC)lpProc);\r
6341     FreeProcInstance(lpProc);\r
6342   }\r
6343 }\r
6344 \r
6345 \r
6346 VOID\r
6347 DisplayMoveError(char *str)\r
6348 {\r
6349   fromX = fromY = -1;\r
6350   ClearHighlights();\r
6351   DrawPosition(FALSE, NULL);\r
6352   if (appData.popupMoveErrors) {\r
6353     DisplayError(str, 0);\r
6354   } else {\r
6355     DisplayMessage(str, "");\r
6356     moveErrorMessageUp = TRUE;\r
6357   }\r
6358 }\r
6359 \r
6360 VOID\r
6361 DisplayFatalError(char *str, int error, int exitStatus)\r
6362 {\r
6363   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
6364   int len;\r
6365   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
6366 \r
6367   if (error != 0) {\r
6368     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
6369                         NULL, error, LANG_NEUTRAL,\r
6370                         (LPSTR) buf2, MSG_SIZ, NULL);\r
6371     if (len > 0) {\r
6372       sprintf(buf, "%s:\n%s", str, buf2);\r
6373     } else {\r
6374       ErrorMap *em = errmap;\r
6375       while (em->err != 0 && em->err != error) em++;\r
6376       if (em->err != 0) {\r
6377         sprintf(buf, "%s:\n%s", str, em->msg);\r
6378       } else {\r
6379         sprintf(buf, "%s:\nError code %d", str, error);\r
6380       }\r
6381     }\r
6382     str = buf;\r
6383   }\r
6384   if (appData.debugMode) {\r
6385     fprintf(debugFP, "%s: %s\n", label, str);\r
6386   }\r
6387   if (appData.popupExitMessage) {\r
6388     (void) MessageBox(hwndMain, str, label, MB_OK|\r
6389                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
6390   }\r
6391   ExitEvent(exitStatus);\r
6392 }\r
6393 \r
6394 \r
6395 VOID\r
6396 DisplayInformation(char *str)\r
6397 {\r
6398   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
6399 }\r
6400 \r
6401 \r
6402 typedef struct {\r
6403   char *title, *question, *replyPrefix;\r
6404   ProcRef pr;\r
6405 } QuestionParams;\r
6406 \r
6407 LRESULT CALLBACK\r
6408 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6409 {\r
6410   static QuestionParams *qp;\r
6411   char reply[MSG_SIZ];\r
6412   int len, err;\r
6413 \r
6414   switch (message) {\r
6415   case WM_INITDIALOG:\r
6416     qp = (QuestionParams *) lParam;\r
6417     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
6418     SetWindowText(hDlg, qp->title);\r
6419     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
6420     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
6421     return FALSE;\r
6422 \r
6423   case WM_COMMAND:\r
6424     switch (LOWORD(wParam)) {\r
6425     case IDOK:\r
6426       strcpy(reply, qp->replyPrefix);\r
6427       if (*reply) strcat(reply, " ");\r
6428       len = strlen(reply);\r
6429       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
6430       strcat(reply, "\n");\r
6431       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
6432       EndDialog(hDlg, TRUE);\r
6433       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
6434       return TRUE;\r
6435     case IDCANCEL:\r
6436       EndDialog(hDlg, FALSE);\r
6437       return TRUE;\r
6438     default:\r
6439       break;\r
6440     }\r
6441     break;\r
6442   }\r
6443   return FALSE;\r
6444 }\r
6445 \r
6446 VOID\r
6447 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
6448 {\r
6449     QuestionParams qp;\r
6450     FARPROC lpProc;\r
6451     \r
6452     qp.title = title;\r
6453     qp.question = question;\r
6454     qp.replyPrefix = replyPrefix;\r
6455     qp.pr = pr;\r
6456     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
6457     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
6458       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
6459     FreeProcInstance(lpProc);\r
6460 }\r
6461 \r
6462 \r
6463 VOID\r
6464 DisplayIcsInteractionTitle(char *str)\r
6465 {\r
6466   char consoleTitle[MSG_SIZ];\r
6467 \r
6468   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
6469   SetWindowText(hwndConsole, consoleTitle);\r
6470 }\r
6471 \r
6472 void\r
6473 DrawPosition(int fullRedraw, Board board)\r
6474 {\r
6475   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
6476 }\r
6477 \r
6478 \r
6479 VOID\r
6480 ResetFrontEnd()\r
6481 {\r
6482   fromX = fromY = -1;\r
6483   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
6484     dragInfo.pos.x = dragInfo.pos.y = -1;\r
6485     dragInfo.pos.x = dragInfo.pos.y = -1;\r
6486     dragInfo.lastpos = dragInfo.pos;\r
6487     dragInfo.start.x = dragInfo.start.y = -1;\r
6488     dragInfo.from = dragInfo.start;\r
6489     ReleaseCapture();\r
6490     DrawPosition(TRUE, NULL);\r
6491   }\r
6492 }\r
6493 \r
6494 \r
6495 VOID\r
6496 CommentPopUp(char *title, char *str)\r
6497 {\r
6498   HWND hwnd = GetActiveWindow();\r
6499   EitherCommentPopUp(0, title, str, FALSE);\r
6500   SetActiveWindow(hwnd);\r
6501 }\r
6502 \r
6503 VOID\r
6504 CommentPopDown(void)\r
6505 {\r
6506   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
6507   if (commentDialog) {\r
6508     ShowWindow(commentDialog, SW_HIDE);\r
6509   }\r
6510   commentDialogUp = FALSE;\r
6511 }\r
6512 \r
6513 VOID\r
6514 EditCommentPopUp(int index, char *title, char *str)\r
6515 {\r
6516   EitherCommentPopUp(index, title, str, TRUE);\r
6517 }\r
6518 \r
6519 \r
6520 VOID\r
6521 RingBell()\r
6522 {\r
6523   MyPlaySound(&sounds[(int)SoundMove]);\r
6524 }\r
6525 \r
6526 VOID PlayIcsWinSound()\r
6527 {\r
6528   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
6529 }\r
6530 \r
6531 VOID PlayIcsLossSound()\r
6532 {\r
6533   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
6534 }\r
6535 \r
6536 VOID PlayIcsDrawSound()\r
6537 {\r
6538   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
6539 }\r
6540 \r
6541 VOID PlayIcsUnfinishedSound()\r
6542 {\r
6543   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
6544 }\r
6545 \r
6546 VOID\r
6547 PlayAlarmSound()\r
6548 {\r
6549   MyPlaySound(&sounds[(int)SoundAlarm]);\r
6550 }\r
6551 \r
6552 \r
6553 VOID\r
6554 EchoOn()\r
6555 {\r
6556   HWND hInput;\r
6557   consoleEcho = TRUE;\r
6558   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6559   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
6560   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
6561 }\r
6562 \r
6563 \r
6564 VOID\r
6565 EchoOff()\r
6566 {\r
6567   CHARFORMAT cf;\r
6568   HWND hInput;\r
6569   consoleEcho = FALSE;\r
6570   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
6571   /* This works OK: set text and background both to the same color */\r
6572   cf = consoleCF;\r
6573   cf.crTextColor = COLOR_ECHOOFF;\r
6574   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
6575   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
6576 }\r
6577 \r
6578 /* No Raw()...? */\r
6579 \r
6580 void Colorize(ColorClass cc, int continuation)\r
6581 {\r
6582   currentColorClass = cc;\r
6583   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
6584   consoleCF.crTextColor = textAttribs[cc].color;\r
6585   consoleCF.dwEffects = textAttribs[cc].effects;\r
6586   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
6587 }\r
6588 \r
6589 char *\r
6590 UserName()\r
6591 {\r
6592   static char buf[MSG_SIZ];\r
6593   DWORD bufsiz = MSG_SIZ;\r
6594 \r
6595   if (!GetUserName(buf, &bufsiz)) {\r
6596     /*DisplayError("Error getting user name", GetLastError());*/\r
6597     strcpy(buf, "User");\r
6598   }\r
6599   return buf;\r
6600 }\r
6601 \r
6602 char *\r
6603 HostName()\r
6604 {\r
6605   static char buf[MSG_SIZ];\r
6606   DWORD bufsiz = MSG_SIZ;\r
6607 \r
6608   if (!GetComputerName(buf, &bufsiz)) {\r
6609     /*DisplayError("Error getting host name", GetLastError());*/\r
6610     strcpy(buf, "Unknown");\r
6611   }\r
6612   return buf;\r
6613 }\r
6614 \r
6615 \r
6616 int\r
6617 ClockTimerRunning()\r
6618 {\r
6619   return clockTimerEvent != 0;\r
6620 }\r
6621 \r
6622 int\r
6623 StopClockTimer()\r
6624 {\r
6625   if (clockTimerEvent == 0) return FALSE;\r
6626   KillTimer(hwndMain, clockTimerEvent);\r
6627   clockTimerEvent = 0;\r
6628   return TRUE;\r
6629 }\r
6630 \r
6631 void\r
6632 StartClockTimer(long millisec)\r
6633 {\r
6634   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
6635                              (UINT) millisec, NULL);\r
6636 }\r
6637 \r
6638 void\r
6639 DisplayWhiteClock(long timeRemaining, int highlight)\r
6640 {\r
6641   HDC hdc;\r
6642   hdc = GetDC(hwndMain);\r
6643   if (!IsIconic(hwndMain)) {\r
6644     DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");\r
6645   }\r
6646   if (highlight && iconCurrent == iconBlack) {\r
6647     iconCurrent = iconWhite;\r
6648     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
6649     if (IsIconic(hwndMain)) {\r
6650       DrawIcon(hdc, 2, 2, iconCurrent);\r
6651     }\r
6652   }\r
6653   (void) ReleaseDC(hwndMain, hdc);\r
6654   if (hwndConsole)\r
6655     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
6656 }\r
6657 \r
6658 void\r
6659 DisplayBlackClock(long timeRemaining, int highlight)\r
6660 {\r
6661   HDC hdc;\r
6662   hdc = GetDC(hwndMain);\r
6663   if (!IsIconic(hwndMain)) {\r
6664     DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");\r
6665   }\r
6666   if (highlight && iconCurrent == iconWhite) {\r
6667     iconCurrent = iconBlack;\r
6668     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
6669     if (IsIconic(hwndMain)) {\r
6670       DrawIcon(hdc, 2, 2, iconCurrent);\r
6671     }\r
6672   }\r
6673   (void) ReleaseDC(hwndMain, hdc);\r
6674   if (hwndConsole)\r
6675     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
6676 }\r
6677 \r
6678 \r
6679 int\r
6680 LoadGameTimerRunning()\r
6681 {\r
6682   return loadGameTimerEvent != 0;\r
6683 }\r
6684 \r
6685 int\r
6686 StopLoadGameTimer()\r
6687 {\r
6688   if (loadGameTimerEvent == 0) return FALSE;\r
6689   KillTimer(hwndMain, loadGameTimerEvent);\r
6690   loadGameTimerEvent = 0;\r
6691   return TRUE;\r
6692 }\r
6693 \r
6694 void\r
6695 StartLoadGameTimer(long millisec)\r
6696 {\r
6697   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
6698                                 (UINT) millisec, NULL);\r
6699 }\r
6700 \r
6701 void\r
6702 AutoSaveGame()\r
6703 {\r
6704   char *defName;\r
6705   FILE *f;\r
6706   char fileTitle[MSG_SIZ];\r
6707 \r
6708   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
6709   f = OpenFileDialog(hwndMain, TRUE, defName,\r
6710                      appData.oldSaveStyle ? "gam" : "pgn",\r
6711                      GAME_FILT, \r
6712                      "Save Game to File", NULL, fileTitle, NULL);\r
6713   if (f != NULL) {\r
6714     SaveGame(f, 0, "");\r
6715     fclose(f);\r
6716   }\r
6717 }\r
6718 \r
6719 \r
6720 void\r
6721 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
6722 {\r
6723   if (delayedTimerEvent != 0) {\r
6724     if (appData.debugMode) {\r
6725       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
6726     }\r
6727     KillTimer(hwndMain, delayedTimerEvent);\r
6728     delayedTimerEvent = 0;\r
6729     delayedTimerCallback();\r
6730   }\r
6731   delayedTimerCallback = cb;\r
6732   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
6733                                 (UINT) millisec, NULL);\r
6734 }\r
6735 \r
6736 DelayedEventCallback\r
6737 GetDelayedEvent()\r
6738 {\r
6739   if (delayedTimerEvent) {\r
6740     return delayedTimerCallback;\r
6741   } else {\r
6742     return NULL;\r
6743   }\r
6744 }\r
6745 \r
6746 void\r
6747 CancelDelayedEvent()\r
6748 {\r
6749   if (delayedTimerEvent) {\r
6750     KillTimer(hwndMain, delayedTimerEvent);\r
6751     delayedTimerEvent = 0;\r
6752   }\r
6753 }\r
6754 \r
6755 /* Start a child process running the given program.\r
6756    The process's standard output can be read from "from", and its\r
6757    standard input can be written to "to".\r
6758    Exit with fatal error if anything goes wrong.\r
6759    Returns an opaque pointer that can be used to destroy the process\r
6760    later.\r
6761 */\r
6762 int\r
6763 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
6764 {\r
6765 #define BUFSIZE 4096\r
6766 \r
6767   HANDLE hChildStdinRd, hChildStdinWr,\r
6768     hChildStdoutRd, hChildStdoutWr;\r
6769   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
6770   SECURITY_ATTRIBUTES saAttr;\r
6771   BOOL fSuccess;\r
6772   PROCESS_INFORMATION piProcInfo;\r
6773   STARTUPINFO siStartInfo;\r
6774   ChildProc *cp;\r
6775   char buf[MSG_SIZ];\r
6776   DWORD err;\r
6777 \r
6778   if (appData.debugMode) {\r
6779     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
6780   }\r
6781 \r
6782   *pr = NoProc;\r
6783 \r
6784   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
6785   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
6786   saAttr.bInheritHandle = TRUE;\r
6787   saAttr.lpSecurityDescriptor = NULL;\r
6788 \r
6789   /*\r
6790    * The steps for redirecting child's STDOUT:\r
6791    *     1. Create anonymous pipe to be STDOUT for child.\r
6792    *     2. Create a noninheritable duplicate of read handle,\r
6793    *         and close the inheritable read handle.\r
6794    */\r
6795 \r
6796   /* Create a pipe for the child's STDOUT. */\r
6797   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
6798     return GetLastError();\r
6799   }\r
6800 \r
6801   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
6802   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
6803                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
6804                              FALSE,     /* not inherited */\r
6805                              DUPLICATE_SAME_ACCESS);\r
6806   if (! fSuccess) {\r
6807     return GetLastError();\r
6808   }\r
6809   CloseHandle(hChildStdoutRd);\r
6810 \r
6811   /*\r
6812    * The steps for redirecting child's STDIN:\r
6813    *     1. Create anonymous pipe to be STDIN for child.\r
6814    *     2. Create a noninheritable duplicate of write handle,\r
6815    *         and close the inheritable write handle.\r
6816    */\r
6817 \r
6818   /* Create a pipe for the child's STDIN. */\r
6819   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
6820     return GetLastError();\r
6821   }\r
6822 \r
6823   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
6824   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
6825                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
6826                              FALSE,     /* not inherited */\r
6827                              DUPLICATE_SAME_ACCESS);\r
6828   if (! fSuccess) {\r
6829     return GetLastError();\r
6830   }\r
6831   CloseHandle(hChildStdinWr);\r
6832 \r
6833   /* Arrange to (1) look in dir for the child .exe file, and\r
6834    * (2) have dir be the child's working directory.  Interpret\r
6835    * dir relative to the directory WinBoard loaded from. */\r
6836   GetCurrentDirectory(MSG_SIZ, buf);\r
6837   SetCurrentDirectory(installDir);\r
6838   SetCurrentDirectory(dir);\r
6839 \r
6840   /* Now create the child process. */\r
6841 \r
6842   siStartInfo.cb = sizeof(STARTUPINFO);\r
6843   siStartInfo.lpReserved = NULL;\r
6844   siStartInfo.lpDesktop = NULL;\r
6845   siStartInfo.lpTitle = NULL;\r
6846   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
6847   siStartInfo.cbReserved2 = 0;\r
6848   siStartInfo.lpReserved2 = NULL;\r
6849   siStartInfo.hStdInput = hChildStdinRd;\r
6850   siStartInfo.hStdOutput = hChildStdoutWr;\r
6851   siStartInfo.hStdError = hChildStdoutWr;\r
6852 \r
6853   fSuccess = CreateProcess(NULL,\r
6854                            cmdLine,        /* command line */\r
6855                            NULL,           /* process security attributes */\r
6856                            NULL,           /* primary thread security attrs */\r
6857                            TRUE,           /* handles are inherited */\r
6858                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
6859                            NULL,           /* use parent's environment */\r
6860                            NULL,\r
6861                            &siStartInfo, /* STARTUPINFO pointer */\r
6862                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
6863 \r
6864   err = GetLastError();\r
6865   SetCurrentDirectory(buf); /* return to prev directory */\r
6866   if (! fSuccess) {\r
6867     return err;\r
6868   }\r
6869 \r
6870   /* Close the handles we don't need in the parent */\r
6871   CloseHandle(piProcInfo.hThread);\r
6872   CloseHandle(hChildStdinRd);\r
6873   CloseHandle(hChildStdoutWr);\r
6874 \r
6875   /* Prepare return value */\r
6876   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
6877   cp->kind = CPReal;\r
6878   cp->hProcess = piProcInfo.hProcess;\r
6879   cp->pid = piProcInfo.dwProcessId;\r
6880   cp->hFrom = hChildStdoutRdDup;\r
6881   cp->hTo = hChildStdinWrDup;\r
6882 \r
6883   *pr = (void *) cp;\r
6884 \r
6885   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
6886      2000 where engines sometimes don't see the initial command(s)\r
6887      from WinBoard and hang.  I don't understand how that can happen,\r
6888      but the Sleep is harmless, so I've put it in.  Others have also\r
6889      reported what may be the same problem, so hopefully this will fix\r
6890      it for them too.  */\r
6891   Sleep(500);\r
6892 \r
6893   return NO_ERROR;\r
6894 }\r
6895 \r
6896 \r
6897 void\r
6898 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
6899 {\r
6900   ChildProc *cp;\r
6901 \r
6902   cp = (ChildProc *) pr;\r
6903   if (cp == NULL) return;\r
6904 \r
6905   switch (cp->kind) {\r
6906   case CPReal:\r
6907     /* TerminateProcess is considered harmful, so... */\r
6908     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
6909     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
6910     /* The following doesn't work because the chess program\r
6911        doesn't "have the same console" as WinBoard.  Maybe\r
6912        we could arrange for this even though neither WinBoard\r
6913        nor the chess program uses a console for stdio? */\r
6914     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
6915     CloseHandle(cp->hProcess);\r
6916     break;\r
6917 \r
6918   case CPComm:\r
6919     if (cp->hFrom) CloseHandle(cp->hFrom);\r
6920     break;\r
6921 \r
6922   case CPSock:\r
6923     closesocket(cp->sock);\r
6924     WSACleanup();\r
6925     break;\r
6926 \r
6927   case CPRcmd:\r
6928     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
6929     closesocket(cp->sock);\r
6930     closesocket(cp->sock2);\r
6931     WSACleanup();\r
6932     break;\r
6933   }\r
6934   free(cp);\r
6935 }\r
6936 \r
6937 void\r
6938 InterruptChildProcess(ProcRef pr)\r
6939 {\r
6940   ChildProc *cp;\r
6941 \r
6942   cp = (ChildProc *) pr;\r
6943   if (cp == NULL) return;\r
6944   switch (cp->kind) {\r
6945   case CPReal:\r
6946     /* The following doesn't work because the chess program\r
6947        doesn't "have the same console" as WinBoard.  Maybe\r
6948        we could arrange for this even though neither WinBoard\r
6949        nor the chess program uses a console for stdio */\r
6950     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
6951     break;\r
6952 \r
6953   case CPComm:\r
6954   case CPSock:\r
6955     /* Can't interrupt */\r
6956     break;\r
6957 \r
6958   case CPRcmd:\r
6959     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
6960     break;\r
6961   }\r
6962 }\r
6963 \r
6964 \r
6965 int\r
6966 OpenTelnet(char *host, char *port, ProcRef *pr)\r
6967 {\r
6968   char cmdLine[MSG_SIZ];\r
6969 \r
6970   if (port[0] == NULLCHAR) {\r
6971     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
6972   } else {\r
6973     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
6974   }\r
6975   return StartChildProcess(cmdLine, "", pr);\r
6976 }\r
6977 \r
6978 \r
6979 /* Code to open TCP sockets */\r
6980 \r
6981 int\r
6982 OpenTCP(char *host, char *port, ProcRef *pr)\r
6983 {\r
6984   ChildProc *cp;\r
6985   int err;\r
6986   SOCKET s;\r
6987   struct sockaddr_in sa, mysa;\r
6988   struct hostent FAR *hp;\r
6989   unsigned short uport;\r
6990   WORD wVersionRequested;\r
6991   WSADATA wsaData;\r
6992 \r
6993   /* Initialize socket DLL */\r
6994   wVersionRequested = MAKEWORD(1, 1);\r
6995   err = WSAStartup(wVersionRequested, &wsaData);\r
6996   if (err != 0) return err;\r
6997 \r
6998   /* Make socket */\r
6999   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
7000     err = WSAGetLastError();\r
7001     WSACleanup();\r
7002     return err;\r
7003   }\r
7004 \r
7005   /* Bind local address using (mostly) don't-care values.\r
7006    */\r
7007   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
7008   mysa.sin_family = AF_INET;\r
7009   mysa.sin_addr.s_addr = INADDR_ANY;\r
7010   uport = (unsigned short) 0;\r
7011   mysa.sin_port = htons(uport);\r
7012   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
7013       == SOCKET_ERROR) {\r
7014     err = WSAGetLastError();\r
7015     WSACleanup();\r
7016     return err;\r
7017   }\r
7018 \r
7019   /* Resolve remote host name */\r
7020   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
7021   if (!(hp = gethostbyname(host))) {\r
7022     unsigned int b0, b1, b2, b3;\r
7023 \r
7024     err = WSAGetLastError();\r
7025 \r
7026     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
7027       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
7028       hp->h_addrtype = AF_INET;\r
7029       hp->h_length = 4;\r
7030       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
7031       hp->h_addr_list[0] = (char *) malloc(4);\r
7032       hp->h_addr_list[0][0] = (char) b0;\r
7033       hp->h_addr_list[0][1] = (char) b1;\r
7034       hp->h_addr_list[0][2] = (char) b2;\r
7035       hp->h_addr_list[0][3] = (char) b3;\r
7036     } else {\r
7037       WSACleanup();\r
7038       return err;\r
7039     }\r
7040   }\r
7041   sa.sin_family = hp->h_addrtype;\r
7042   uport = (unsigned short) atoi(port);\r
7043   sa.sin_port = htons(uport);\r
7044   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
7045 \r
7046   /* Make connection */\r
7047   if (connect(s, (struct sockaddr *) &sa,\r
7048               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
7049     err = WSAGetLastError();\r
7050     WSACleanup();\r
7051     return err;\r
7052   }\r
7053 \r
7054   /* Prepare return value */\r
7055   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
7056   cp->kind = CPSock;\r
7057   cp->sock = s;\r
7058   *pr = (ProcRef *) cp;\r
7059 \r
7060   return NO_ERROR;\r
7061 }\r
7062 \r
7063 int\r
7064 OpenCommPort(char *name, ProcRef *pr)\r
7065 {\r
7066   HANDLE h;\r
7067   COMMTIMEOUTS ct;\r
7068   ChildProc *cp;\r
7069   char fullname[MSG_SIZ];\r
7070 \r
7071   if (*name != '\\')\r
7072     sprintf(fullname, "\\\\.\\%s", name);\r
7073   else\r
7074     strcpy(fullname, name);\r
7075 \r
7076   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
7077                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
7078   if (h == (HANDLE) -1) {\r
7079     return GetLastError();\r
7080   }\r
7081   hCommPort = h;\r
7082 \r
7083   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
7084 \r
7085   /* Accumulate characters until a 100ms pause, then parse */\r
7086   ct.ReadIntervalTimeout = 100;\r
7087   ct.ReadTotalTimeoutMultiplier = 0;\r
7088   ct.ReadTotalTimeoutConstant = 0;\r
7089   ct.WriteTotalTimeoutMultiplier = 0;\r
7090   ct.WriteTotalTimeoutConstant = 0;\r
7091   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
7092 \r
7093   /* Prepare return value */\r
7094   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
7095   cp->kind = CPComm;\r
7096   cp->hFrom = h;\r
7097   cp->hTo = h;\r
7098   *pr = (ProcRef *) cp;\r
7099 \r
7100   return NO_ERROR;\r
7101 }\r
7102 \r
7103 int\r
7104 OpenLoopback(ProcRef *pr)\r
7105 {\r
7106   DisplayFatalError("Not implemented", 0, 1);\r
7107   return NO_ERROR;\r
7108 }\r
7109 \r
7110 \r
7111 int\r
7112 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
7113 {\r
7114   ChildProc *cp;\r
7115   int err;\r
7116   SOCKET s, s2, s3;\r
7117   struct sockaddr_in sa, mysa;\r
7118   struct hostent FAR *hp;\r
7119   unsigned short uport;\r
7120   WORD wVersionRequested;\r
7121   WSADATA wsaData;\r
7122   int fromPort;\r
7123   char stderrPortStr[MSG_SIZ];\r
7124 \r
7125   /* Initialize socket DLL */\r
7126   wVersionRequested = MAKEWORD(1, 1);\r
7127   err = WSAStartup(wVersionRequested, &wsaData);\r
7128   if (err != 0) return err;\r
7129 \r
7130   /* Resolve remote host name */\r
7131   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
7132   if (!(hp = gethostbyname(host))) {\r
7133     unsigned int b0, b1, b2, b3;\r
7134 \r
7135     err = WSAGetLastError();\r
7136 \r
7137     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
7138       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
7139       hp->h_addrtype = AF_INET;\r
7140       hp->h_length = 4;\r
7141       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
7142       hp->h_addr_list[0] = (char *) malloc(4);\r
7143       hp->h_addr_list[0][0] = (char) b0;\r
7144       hp->h_addr_list[0][1] = (char) b1;\r
7145       hp->h_addr_list[0][2] = (char) b2;\r
7146       hp->h_addr_list[0][3] = (char) b3;\r
7147     } else {\r
7148       WSACleanup();\r
7149       return err;\r
7150     }\r
7151   }\r
7152   sa.sin_family = hp->h_addrtype;\r
7153   uport = (unsigned short) 514;\r
7154   sa.sin_port = htons(uport);\r
7155   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
7156 \r
7157   /* Bind local socket to unused "privileged" port address\r
7158    */\r
7159   s = INVALID_SOCKET;\r
7160   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
7161   mysa.sin_family = AF_INET;\r
7162   mysa.sin_addr.s_addr = INADDR_ANY;\r
7163   for (fromPort = 1023;; fromPort--) {\r
7164     if (fromPort < 0) {\r
7165       WSACleanup();\r
7166       return WSAEADDRINUSE;\r
7167     }\r
7168     if (s == INVALID_SOCKET) {\r
7169       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
7170         err = WSAGetLastError();\r
7171         WSACleanup();\r
7172         return err;\r
7173       }\r
7174     }\r
7175     uport = (unsigned short) fromPort;\r
7176     mysa.sin_port = htons(uport);\r
7177     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
7178         == SOCKET_ERROR) {\r
7179       err = WSAGetLastError();\r
7180       if (err == WSAEADDRINUSE) continue;\r
7181       WSACleanup();\r
7182       return err;\r
7183     }\r
7184     if (connect(s, (struct sockaddr *) &sa,\r
7185       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
7186       err = WSAGetLastError();\r
7187       if (err == WSAEADDRINUSE) {\r
7188         closesocket(s);\r
7189         s = -1;\r
7190         continue;\r
7191       }\r
7192       WSACleanup();\r
7193       return err;\r
7194     }\r
7195     break;\r
7196   }\r
7197 \r
7198   /* Bind stderr local socket to unused "privileged" port address\r
7199    */\r
7200   s2 = INVALID_SOCKET;\r
7201   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
7202   mysa.sin_family = AF_INET;\r
7203   mysa.sin_addr.s_addr = INADDR_ANY;\r
7204   for (fromPort = 1023;; fromPort--) {\r
7205     if (fromPort < 0) {\r
7206       (void) closesocket(s);\r
7207       WSACleanup();\r
7208       return WSAEADDRINUSE;\r
7209     }\r
7210     if (s2 == INVALID_SOCKET) {\r
7211       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
7212         err = WSAGetLastError();\r
7213         closesocket(s);\r
7214         WSACleanup();\r
7215         return err;\r
7216       }\r
7217     }\r
7218     uport = (unsigned short) fromPort;\r
7219     mysa.sin_port = htons(uport);\r
7220     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
7221         == SOCKET_ERROR) {\r
7222       err = WSAGetLastError();\r
7223       if (err == WSAEADDRINUSE) continue;\r
7224       (void) closesocket(s);\r
7225       WSACleanup();\r
7226       return err;\r
7227     }\r
7228     if (listen(s2, 1) == SOCKET_ERROR) {\r
7229       err = WSAGetLastError();\r
7230       if (err == WSAEADDRINUSE) {\r
7231         closesocket(s2);\r
7232         s2 = INVALID_SOCKET;\r
7233         continue;\r
7234       }\r
7235       (void) closesocket(s);\r
7236       (void) closesocket(s2);\r
7237       WSACleanup();\r
7238       return err;\r
7239     }\r
7240     break;\r
7241   }\r
7242   sprintf(stderrPortStr, "%d", fromPort);\r
7243 \r
7244   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
7245     err = WSAGetLastError();\r
7246     (void) closesocket(s);\r
7247     (void) closesocket(s2);\r
7248     WSACleanup();\r
7249     return err;\r
7250   }\r
7251 \r
7252   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
7253     err = WSAGetLastError();\r
7254     (void) closesocket(s);\r
7255     (void) closesocket(s2);\r
7256     WSACleanup();\r
7257     return err;\r
7258   }\r
7259   if (*user == NULLCHAR) user = UserName();\r
7260   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
7261     err = WSAGetLastError();\r
7262     (void) closesocket(s);\r
7263     (void) closesocket(s2);\r
7264     WSACleanup();\r
7265     return err;\r
7266   }\r
7267   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
7268     err = WSAGetLastError();\r
7269     (void) closesocket(s);\r
7270     (void) closesocket(s2);\r
7271     WSACleanup();\r
7272     return err;\r
7273   }\r
7274 \r
7275   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
7276     err = WSAGetLastError();\r
7277     (void) closesocket(s);\r
7278     (void) closesocket(s2);\r
7279     WSACleanup();\r
7280     return err;\r
7281   }\r
7282   (void) closesocket(s2);  /* Stop listening */\r
7283 \r
7284   /* Prepare return value */\r
7285   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
7286   cp->kind = CPRcmd;\r
7287   cp->sock = s;\r
7288   cp->sock2 = s3;\r
7289   *pr = (ProcRef *) cp;\r
7290 \r
7291   return NO_ERROR;\r
7292 }\r
7293 \r
7294 \r
7295 InputSourceRef\r
7296 AddInputSource(ProcRef pr, int lineByLine,\r
7297                InputCallback func, VOIDSTAR closure)\r
7298 {\r
7299   InputSource *is, *is2;\r
7300   ChildProc *cp = (ChildProc *) pr;\r
7301 \r
7302   is = (InputSource *) calloc(1, sizeof(InputSource));\r
7303   is->lineByLine = lineByLine;\r
7304   is->func = func;\r
7305   is->closure = closure;\r
7306   is->second = NULL;\r
7307   is->next = is->buf;\r
7308   if (pr == NoProc) {\r
7309     is->kind = CPReal;\r
7310     consoleInputSource = is;\r
7311   } else {\r
7312     is->kind = cp->kind;\r
7313     switch (cp->kind) {\r
7314     case CPReal:\r
7315       is->hFile = cp->hFrom;\r
7316       cp->hFrom = NULL; /* now owned by InputThread */\r
7317       is->hThread =\r
7318         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
7319                      (LPVOID) is, 0, &is->id);\r
7320       break;\r
7321 \r
7322     case CPComm:\r
7323       is->hFile = cp->hFrom;\r
7324       cp->hFrom = NULL; /* now owned by InputThread */\r
7325       is->hThread =\r
7326         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
7327                      (LPVOID) is, 0, &is->id);\r
7328       break;\r
7329 \r
7330     case CPSock:\r
7331       is->sock = cp->sock;\r
7332       is->hThread =\r
7333         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
7334                      (LPVOID) is, 0, &is->id);\r
7335       break;\r
7336 \r
7337     case CPRcmd:\r
7338       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
7339       *is2 = *is;\r
7340       is->sock = cp->sock;\r
7341       is->second = is2;\r
7342       is2->sock = cp->sock2;\r
7343       is2->second = is2;\r
7344       is->hThread =\r
7345         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
7346                      (LPVOID) is, 0, &is->id);\r
7347       is2->hThread =\r
7348         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
7349                      (LPVOID) is2, 0, &is2->id);\r
7350       break;\r
7351     }\r
7352   }\r
7353   return (InputSourceRef) is;\r
7354 }\r
7355 \r
7356 void\r
7357 RemoveInputSource(InputSourceRef isr)\r
7358 {\r
7359   InputSource *is;\r
7360 \r
7361   is = (InputSource *) isr;\r
7362   is->hThread = NULL;  /* tell thread to stop */\r
7363   CloseHandle(is->hThread);\r
7364   if (is->second != NULL) {\r
7365     is->second->hThread = NULL;\r
7366     CloseHandle(is->second->hThread);\r
7367   }\r
7368 }\r
7369 \r
7370 \r
7371 int\r
7372 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
7373 {\r
7374   DWORD dOutCount;\r
7375   int outCount = SOCKET_ERROR;\r
7376   ChildProc *cp = (ChildProc *) pr;\r
7377   static OVERLAPPED ovl;\r
7378 \r
7379   if (pr == NoProc) {\r
7380     ConsoleOutput(message, count, FALSE);\r
7381     return count;\r
7382   } \r
7383 \r
7384   if (ovl.hEvent == NULL) {\r
7385     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
7386   }\r
7387   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
7388 \r
7389   switch (cp->kind) {\r
7390   case CPSock:\r
7391   case CPRcmd:\r
7392     outCount = send(cp->sock, message, count, 0);\r
7393     if (outCount == SOCKET_ERROR) {\r
7394       *outError = WSAGetLastError();\r
7395     } else {\r
7396       *outError = NO_ERROR;\r
7397     }\r
7398     break;\r
7399 \r
7400   case CPReal:\r
7401     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
7402                   &dOutCount, NULL)) {\r
7403       *outError = NO_ERROR;\r
7404       outCount = (int) dOutCount;\r
7405     } else {\r
7406       *outError = GetLastError();\r
7407     }\r
7408     break;\r
7409 \r
7410   case CPComm:\r
7411     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
7412                             &dOutCount, &ovl);\r
7413     if (*outError == NO_ERROR) {\r
7414       outCount = (int) dOutCount;\r
7415     }\r
7416     break;\r
7417   }\r
7418   return outCount;\r
7419 }\r
7420 \r
7421 int\r
7422 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
7423                        long msdelay)\r
7424 {\r
7425   /* Ignore delay, not implemented for WinBoard */\r
7426   return OutputToProcess(pr, message, count, outError);\r
7427 }\r
7428 \r
7429 \r
7430 void\r
7431 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
7432                         char *buf, int count, int error)\r
7433 {\r
7434   DisplayFatalError("Not implemented", 0, 1);\r
7435 }\r
7436 \r
7437 /* see wgamelist.c for Game List functions */\r
7438 /* see wedittags.c for Edit Tags functions */\r
7439 \r
7440 \r
7441 VOID\r
7442 ICSInitScript()\r
7443 {\r
7444   FILE *f;\r
7445   char buf[MSG_SIZ];\r
7446   char *dummy;\r
7447 \r
7448   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
7449     f = fopen(buf, "r");\r
7450     if (f != NULL) {\r
7451       ProcessICSInitScript(f);\r
7452       fclose(f);\r
7453     }\r
7454   }\r
7455 }\r
7456 \r
7457 \r
7458 VOID\r
7459 StartAnalysisClock()\r
7460 {\r
7461   if (analysisTimerEvent) return;\r
7462   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
7463                                         (UINT) 2000, NULL);\r
7464 }\r
7465 \r
7466 LRESULT CALLBACK\r
7467 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7468 {\r
7469   static HANDLE hwndText;\r
7470   RECT rect;\r
7471   static int sizeX, sizeY;\r
7472   int newSizeX, newSizeY, flags;\r
7473   MINMAXINFO *mmi;\r
7474 \r
7475   switch (message) {\r
7476   case WM_INITDIALOG: /* message: initialize dialog box */\r
7477     /* Initialize the dialog items */\r
7478     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
7479     SetWindowText(hDlg, analysisTitle);\r
7480     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
7481     /* Size and position the dialog */\r
7482     if (!analysisDialog) {\r
7483       analysisDialog = hDlg;\r
7484       flags = SWP_NOZORDER;\r
7485       GetClientRect(hDlg, &rect);\r
7486       sizeX = rect.right;\r
7487       sizeY = rect.bottom;\r
7488       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
7489           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
7490         WINDOWPLACEMENT wp;\r
7491         EnsureOnScreen(&analysisX, &analysisY);\r
7492         wp.length = sizeof(WINDOWPLACEMENT);\r
7493         wp.flags = 0;\r
7494         wp.showCmd = SW_SHOW;\r
7495         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7496         wp.rcNormalPosition.left = analysisX;\r
7497         wp.rcNormalPosition.right = analysisX + analysisW;\r
7498         wp.rcNormalPosition.top = analysisY;\r
7499         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
7500         SetWindowPlacement(hDlg, &wp);\r
7501 \r
7502         GetClientRect(hDlg, &rect);\r
7503         newSizeX = rect.right;\r
7504         newSizeY = rect.bottom;\r
7505         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7506                               newSizeX, newSizeY);\r
7507         sizeX = newSizeX;\r
7508         sizeY = newSizeY;\r
7509       }\r
7510     }\r
7511     return FALSE;\r
7512 \r
7513   case WM_COMMAND: /* message: received a command */\r
7514     switch (LOWORD(wParam)) {\r
7515     case IDCANCEL:\r
7516       EditGameEvent();\r
7517       return TRUE;\r
7518     default:\r
7519       break;\r
7520     }\r
7521     break;\r
7522 \r
7523   case WM_SIZE:\r
7524     newSizeX = LOWORD(lParam);\r
7525     newSizeY = HIWORD(lParam);\r
7526     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7527     sizeX = newSizeX;\r
7528     sizeY = newSizeY;\r
7529     break;\r
7530 \r
7531   case WM_GETMINMAXINFO:\r
7532     /* Prevent resizing window too small */\r
7533     mmi = (MINMAXINFO *) lParam;\r
7534     mmi->ptMinTrackSize.x = 100;\r
7535     mmi->ptMinTrackSize.y = 100;\r
7536     break;\r
7537   }\r
7538   return FALSE;\r
7539 }\r
7540 \r
7541 VOID\r
7542 AnalysisPopUp(char* title, char* str)\r
7543 {\r
7544   FARPROC lpProc;\r
7545   char *p, *q;\r
7546 \r
7547   if (str == NULL) str = "";\r
7548   p = (char *) malloc(2 * strlen(str) + 2);\r
7549   q = p;\r
7550   while (*str) {\r
7551     if (*str == '\n') *q++ = '\r';\r
7552     *q++ = *str++;\r
7553   }\r
7554   *q = NULLCHAR;\r
7555   if (analysisText != NULL) free(analysisText);\r
7556   analysisText = p;\r
7557 \r
7558   if (analysisDialog) {\r
7559     SetWindowText(analysisDialog, title);\r
7560     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
7561     ShowWindow(analysisDialog, SW_SHOW);\r
7562   } else {\r
7563     analysisTitle = title;\r
7564     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
7565     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
7566                  hwndMain, (DLGPROC)lpProc);\r
7567     FreeProcInstance(lpProc);\r
7568   }\r
7569   analysisDialogUp = TRUE;  \r
7570 }\r
7571 \r
7572 VOID\r
7573 AnalysisPopDown()\r
7574 {\r
7575   if (analysisDialog) {\r
7576     ShowWindow(analysisDialog, SW_HIDE);\r
7577   }\r
7578   analysisDialogUp = FALSE;  \r
7579 }\r
7580 \r
7581 \r
7582 VOID\r
7583 SetHighlights(int fromX, int fromY, int toX, int toY)\r
7584 {\r
7585   highlightInfo.sq[0].x = fromX;\r
7586   highlightInfo.sq[0].y = fromY;\r
7587   highlightInfo.sq[1].x = toX;\r
7588   highlightInfo.sq[1].y = toY;\r
7589 }\r
7590 \r
7591 VOID\r
7592 ClearHighlights()\r
7593 {\r
7594   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
7595     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
7596 }\r
7597 \r
7598 VOID\r
7599 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
7600 {\r
7601   premoveHighlightInfo.sq[0].x = fromX;\r
7602   premoveHighlightInfo.sq[0].y = fromY;\r
7603   premoveHighlightInfo.sq[1].x = toX;\r
7604   premoveHighlightInfo.sq[1].y = toY;\r
7605 }\r
7606 \r
7607 VOID\r
7608 ClearPremoveHighlights()\r
7609 {\r
7610   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
7611     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
7612 }\r
7613 \r
7614 VOID\r
7615 ShutDownFrontEnd()\r
7616 {\r
7617   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
7618   DeleteClipboardTempFiles();\r
7619 }\r
7620 \r
7621 void\r
7622 BoardToTop()\r
7623 {\r
7624     if (IsIconic(hwndMain))\r
7625       ShowWindow(hwndMain, SW_RESTORE);\r
7626 \r
7627     SetActiveWindow(hwndMain);\r
7628 }\r
7629 \r
7630 /*\r
7631  * Prototypes for animation support routines\r
7632  */\r
7633 static void ScreenSquare(int column, int row, POINT * pt);\r
7634 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
7635      POINT frames[], int * nFrames);\r
7636 \r
7637 \r
7638 #define kFactor 4\r
7639 \r
7640 void\r
7641 AnimateMove(board, fromX, fromY, toX, toY)\r
7642      Board board;\r
7643      int fromX;\r
7644      int fromY;\r
7645      int toX;\r
7646      int toY;\r
7647 {\r
7648   ChessSquare piece;\r
7649   POINT start, finish, mid;\r
7650   POINT frames[kFactor * 2 + 1];\r
7651   int nFrames, n;\r
7652 \r
7653   if (!appData.animate) return;\r
7654   if (doingSizing) return;\r
7655   if (fromY < 0 || fromX < 0) return;\r
7656   piece = board[fromY][fromX];\r
7657   if (piece >= EmptySquare) return;\r
7658 \r
7659   ScreenSquare(fromX, fromY, &start);\r
7660   ScreenSquare(toX, toY, &finish);\r
7661 \r
7662   /* All pieces except knights move in straight line */\r
7663   if (piece != WhiteKnight && piece != BlackKnight) {\r
7664     mid.x = start.x + (finish.x - start.x) / 2;\r
7665     mid.y = start.y + (finish.y - start.y) / 2;\r
7666   } else {\r
7667     /* Knight: make diagonal movement then straight */\r
7668     if (abs(toY - fromY) < abs(toX - fromX)) {\r
7669        mid.x = start.x + (finish.x - start.x) / 2;\r
7670        mid.y = finish.y;\r
7671      } else {\r
7672        mid.x = finish.x;\r
7673        mid.y = start.y + (finish.y - start.y) / 2;\r
7674      }\r
7675   }\r
7676   \r
7677   /* Don't use as many frames for very short moves */\r
7678   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
7679     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
7680   else\r
7681     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
7682 \r
7683   animInfo.from.x = fromX;\r
7684   animInfo.from.y = fromY;\r
7685   animInfo.to.x = toX;\r
7686   animInfo.to.y = toY;\r
7687   animInfo.lastpos = start;\r
7688   animInfo.piece = piece;\r
7689   for (n = 0; n < nFrames; n++) {\r
7690     animInfo.pos = frames[n];\r
7691     DrawPosition(FALSE, NULL);\r
7692     animInfo.lastpos = animInfo.pos;\r
7693     Sleep(appData.animSpeed);\r
7694   }\r
7695   animInfo.pos = finish;\r
7696   DrawPosition(FALSE, NULL);\r
7697   animInfo.piece = EmptySquare;\r
7698 }\r
7699 \r
7700 /*      Convert board position to corner of screen rect and color       */\r
7701 \r
7702 static void\r
7703 ScreenSquare(column, row, pt)\r
7704      int column; int row; POINT * pt;\r
7705 {\r
7706   if (flipView) {\r
7707     pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);\r
7708     pt->y = lineGap + row * (squareSize + lineGap);\r
7709   } else {\r
7710     pt->x = lineGap + column * (squareSize + lineGap);\r
7711     pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);\r
7712   }\r
7713 }\r
7714 \r
7715 /*      Generate a series of frame coords from start->mid->finish.\r
7716         The movement rate doubles until the half way point is\r
7717         reached, then halves back down to the final destination,\r
7718         which gives a nice slow in/out effect. The algorithmn\r
7719         may seem to generate too many intermediates for short\r
7720         moves, but remember that the purpose is to attract the\r
7721         viewers attention to the piece about to be moved and\r
7722         then to where it ends up. Too few frames would be less\r
7723         noticeable.                                             */\r
7724 \r
7725 static void\r
7726 Tween(start, mid, finish, factor, frames, nFrames)\r
7727      POINT * start; POINT * mid;\r
7728      POINT * finish; int factor;\r
7729      POINT frames[]; int * nFrames;\r
7730 {\r
7731   int n, fraction = 1, count = 0;\r
7732 \r
7733   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
7734   for (n = 0; n < factor; n++)\r
7735     fraction *= 2;\r
7736   for (n = 0; n < factor; n++) {\r
7737     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
7738     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
7739     count ++;\r
7740     fraction = fraction / 2;\r
7741   }\r
7742   \r
7743   /* Midpoint */\r
7744   frames[count] = *mid;\r
7745   count ++;\r
7746   \r
7747   /* Slow out, stepping 1/2, then 1/4, ... */\r
7748   fraction = 2;\r
7749   for (n = 0; n < factor; n++) {\r
7750     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
7751     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
7752     count ++;\r
7753     fraction = fraction * 2;\r
7754   }\r
7755   *nFrames = count;\r
7756 }\r
7757 \r
7758 void\r
7759 HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)\r
7760 {\r
7761   /* Currently not implemented in WinBoard */\r
7762 }\r
7763 \r
7764 \r