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