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