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