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