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