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