Added ICS engine analyze for winboard - not ready but usable
[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                                 appData.icsEngineAnalyze = FALSE;
3654                                 ExitAnalyzeMode();
3655                                 ModeHighlight();
3656                                 break;
3657                         }
3658                         break;
3659                 } else {
3660                         /* if enable, user want disable icsEngineAnalyze */
3661                         if (appData.icsEngineAnalyze) {
3662                                 appData.icsEngineAnalyze = FALSE;
3663                                 ExitAnalyzeMode();
3664                                 ModeHighlight();
3665                                 break;
3666                         }
3667                         appData.icsEngineAnalyze = TRUE;
3668                 }
3669         } 
3670         if (!appData.showThinking) ToggleShowThinking();
3671         AnalyzeModeEvent();
3672       }
3673       break;
3674     case IDM_AnalyzeFile:
3675       if (!first.analysisSupport) {
3676         char buf[MSG_SIZ];
3677         sprintf(buf, "%s does not support analysis", first.tidy);
3678         DisplayError(buf, 0);
3679       } else {
3680         if (!appData.showThinking) ToggleShowThinking();
3681         AnalyzeFileEvent();
3682         LoadGameDialog(hwnd, "Analyze Game from File");
3683         AnalysisPeriodicEvent(1);
3684       }
3685       break;
3686
3687     case IDM_IcsClient:
3688       IcsClientEvent();
3689       break;
3690
3691     case IDM_EditGame:
3692       EditGameEvent();
3693       break;
3694
3695     case IDM_EditPosition:
3696       EditPositionEvent();
3697       break;
3698
3699     case IDM_Training:
3700       TrainingEvent();
3701       break;
3702
3703     case IDM_ShowGameList:
3704       ShowGameListProc();
3705       break;
3706
3707     case IDM_EditTags:
3708       EditTagsProc();
3709       break;
3710
3711     case IDM_EditComment:
3712       if (commentDialogUp && editComment) {
3713         CommentPopDown();
3714       } else {
3715         EditCommentEvent();
3716       }
3717       break;
3718
3719     case IDM_Pause:
3720       PauseEvent();
3721       break;
3722
3723     case IDM_Accept:
3724       AcceptEvent();
3725       break;
3726
3727     case IDM_Decline:
3728       DeclineEvent();
3729       break;
3730
3731     case IDM_Rematch:
3732       RematchEvent();
3733       break;
3734
3735     case IDM_CallFlag:
3736       CallFlagEvent();
3737       break;
3738
3739     case IDM_Draw:
3740       DrawEvent();
3741       break;
3742
3743     case IDM_Adjourn:
3744       AdjournEvent();
3745       break;
3746
3747     case IDM_Abort:
3748       AbortEvent();
3749       break;
3750
3751     case IDM_Resign:
3752       ResignEvent();
3753       break;
3754
3755     case IDM_StopObserving:
3756       StopObservingEvent();
3757       break;
3758
3759     case IDM_StopExamining:
3760       StopExaminingEvent();
3761       break;
3762
3763     case IDM_TypeInMove:
3764       PopUpMoveDialog('\000');
3765       break;
3766
3767     case IDM_Backward:
3768       BackwardEvent();
3769       SetFocus(hwndMain);
3770       break;
3771
3772     case IDM_Forward:
3773       ForwardEvent();
3774       SetFocus(hwndMain);
3775       break;
3776
3777     case IDM_ToStart:
3778       ToStartEvent();
3779       SetFocus(hwndMain);
3780       break;
3781
3782     case IDM_ToEnd:
3783       ToEndEvent();
3784       SetFocus(hwndMain);
3785       break;
3786
3787     case IDM_Revert:
3788       RevertEvent();
3789       break;
3790
3791     case IDM_TruncateGame:
3792       TruncateGameEvent();
3793       break;
3794
3795     case IDM_MoveNow:
3796       MoveNowEvent();
3797       break;
3798
3799     case IDM_RetractMove:
3800       RetractMoveEvent();
3801       break;
3802
3803     case IDM_FlipView:
3804       flipView = !flipView;
3805       DrawPosition(FALSE, NULL);
3806       break;
3807
3808     case IDM_GeneralOptions:
3809       GeneralOptionsPopup(hwnd);
3810       break;
3811
3812     case IDM_BoardOptions:
3813       BoardOptionsPopup(hwnd);
3814       break;
3815
3816     case IDM_IcsOptions:
3817       IcsOptionsPopup(hwnd);
3818       break;
3819
3820     case IDM_Fonts:
3821       FontsOptionsPopup(hwnd);
3822       break;
3823
3824     case IDM_Sounds:
3825       SoundOptionsPopup(hwnd);
3826       break;
3827
3828     case IDM_CommPort:
3829       CommPortOptionsPopup(hwnd);
3830       break;
3831
3832     case IDM_LoadOptions:
3833       LoadOptionsPopup(hwnd);
3834       break;
3835
3836     case IDM_SaveOptions:
3837       SaveOptionsPopup(hwnd);
3838       break;
3839
3840     case IDM_TimeControl:
3841       TimeControlOptionsPopup(hwnd);
3842       break;
3843
3844     case IDM_SaveSettings:
3845       SaveSettings(settingsFileName);
3846       break;
3847
3848     case IDM_SaveSettingsOnExit:
3849       saveSettingsOnExit = !saveSettingsOnExit;
3850       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
3851                            MF_BYCOMMAND|(saveSettingsOnExit ?
3852                                          MF_CHECKED : MF_UNCHECKED));
3853       break;
3854
3855     case IDM_Hint:
3856       HintEvent();
3857       break;
3858
3859     case IDM_Book:
3860       BookEvent();
3861       break;
3862
3863     case IDM_AboutGame:
3864       AboutGameEvent();
3865       break;
3866
3867     case IDM_Debug:
3868       appData.debugMode = !appData.debugMode;
3869       if (appData.debugMode) {
3870         char dir[MSG_SIZ];
3871         GetCurrentDirectory(MSG_SIZ, dir);
3872         SetCurrentDirectory(installDir);
3873         debugFP = fopen("WinBoard.debug", "w");
3874         SetCurrentDirectory(dir);
3875         setbuf(debugFP, NULL);
3876       } else {
3877         fclose(debugFP);
3878         debugFP = NULL;
3879       }
3880       break;
3881
3882     case IDM_HELPCONTENTS:
3883       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
3884         MessageBox (GetFocus(),
3885                     "Unable to activate help",
3886                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3887       }
3888       break;
3889
3890     case IDM_HELPSEARCH:
3891       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
3892         MessageBox (GetFocus(),
3893                     "Unable to activate help",
3894                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3895       }
3896       break;
3897
3898     case IDM_HELPHELP:
3899       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
3900         MessageBox (GetFocus(),
3901                     "Unable to activate help",
3902                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3903       }
3904       break;
3905
3906     case IDM_ABOUT:
3907       lpProc = MakeProcInstance((FARPROC)About, hInst);
3908       DialogBox(hInst, 
3909         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
3910         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
3911       FreeProcInstance(lpProc);
3912       break;
3913
3914     case IDM_DirectCommand1:
3915       AskQuestionEvent("Direct Command",
3916                        "Send to chess program:", "", "1");
3917       break;
3918     case IDM_DirectCommand2:
3919       AskQuestionEvent("Direct Command",
3920                        "Send to second chess program:", "", "2");
3921       break;
3922
3923     case EP_WhitePawn:
3924       EditPositionMenuEvent(WhitePawn, fromX, fromY);
3925       fromX = fromY = -1;
3926       break;
3927
3928     case EP_WhiteKnight:
3929       EditPositionMenuEvent(WhiteKnight, fromX, fromY);
3930       fromX = fromY = -1;
3931       break;
3932
3933     case EP_WhiteBishop:
3934       EditPositionMenuEvent(WhiteBishop, fromX, fromY);
3935       fromX = fromY = -1;
3936       break;
3937
3938     case EP_WhiteRook:
3939       EditPositionMenuEvent(WhiteRook, fromX, fromY);
3940       fromX = fromY = -1;
3941       break;
3942
3943     case EP_WhiteQueen:
3944       EditPositionMenuEvent(WhiteQueen, fromX, fromY);
3945       fromX = fromY = -1;
3946       break;
3947
3948     case EP_WhiteKing:
3949       EditPositionMenuEvent(WhiteKing, fromX, fromY);
3950       fromX = fromY = -1;
3951       break;
3952
3953     case EP_BlackPawn:
3954       EditPositionMenuEvent(BlackPawn, fromX, fromY);
3955       fromX = fromY = -1;
3956       break;
3957
3958     case EP_BlackKnight:
3959       EditPositionMenuEvent(BlackKnight, fromX, fromY);
3960       fromX = fromY = -1;
3961       break;
3962
3963     case EP_BlackBishop:
3964       EditPositionMenuEvent(BlackBishop, fromX, fromY);
3965       fromX = fromY = -1;
3966       break;
3967
3968     case EP_BlackRook:
3969       EditPositionMenuEvent(BlackRook, fromX, fromY);
3970       fromX = fromY = -1;
3971       break;
3972
3973     case EP_BlackQueen:
3974       EditPositionMenuEvent(BlackQueen, fromX, fromY);
3975       fromX = fromY = -1;
3976       break;
3977
3978     case EP_BlackKing:
3979       EditPositionMenuEvent(BlackKing, fromX, fromY);
3980       fromX = fromY = -1;
3981       break;
3982
3983     case EP_EmptySquare:
3984       EditPositionMenuEvent(EmptySquare, fromX, fromY);
3985       fromX = fromY = -1;
3986       break;
3987
3988     case EP_ClearBoard:
3989       EditPositionMenuEvent(ClearBoard, fromX, fromY);
3990       fromX = fromY = -1;
3991       break;
3992
3993     case EP_White:
3994       EditPositionMenuEvent(WhitePlay, fromX, fromY);
3995       fromX = fromY = -1;
3996       break;
3997
3998     case EP_Black:
3999       EditPositionMenuEvent(BlackPlay, fromX, fromY);
4000       fromX = fromY = -1;
4001       break;
4002
4003     case DP_Pawn:
4004       DropMenuEvent(WhitePawn, fromX, fromY);
4005       fromX = fromY = -1;
4006       break;
4007
4008     case DP_Knight:
4009       DropMenuEvent(WhiteKnight, fromX, fromY);
4010       fromX = fromY = -1;
4011       break;
4012
4013     case DP_Bishop:
4014       DropMenuEvent(WhiteBishop, fromX, fromY);
4015       fromX = fromY = -1;
4016       break;
4017
4018     case DP_Rook:
4019       DropMenuEvent(WhiteRook, fromX, fromY);
4020       fromX = fromY = -1;
4021       break;
4022
4023     case DP_Queen:
4024       DropMenuEvent(WhiteQueen, fromX, fromY);
4025       fromX = fromY = -1;
4026       break;
4027
4028     default:
4029       return (DefWindowProc(hwnd, message, wParam, lParam));
4030     }
4031     break;
4032
4033   case WM_TIMER:
4034     switch (wParam) {
4035     case CLOCK_TIMER_ID:
4036       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */
4037       clockTimerEvent = 0;
4038       DecrementClocks(); /* call into back end */
4039       break;
4040     case LOAD_GAME_TIMER_ID:
4041       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
4042       loadGameTimerEvent = 0;
4043       AutoPlayGameLoop(); /* call into back end */
4044       break;
4045     case ANALYSIS_TIMER_ID:
4046       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile
4047                   || appData.icsEngineAnalyze) && appData.periodicUpdates) {
4048         AnalysisPeriodicEvent(0);
4049       } else {
4050         KillTimer(hwnd, analysisTimerEvent);
4051         analysisTimerEvent = 0;
4052       }
4053       break;
4054     case DELAYED_TIMER_ID:
4055       KillTimer(hwnd, delayedTimerEvent);
4056       delayedTimerEvent = 0;
4057       delayedTimerCallback();
4058       break;
4059     }
4060     break;
4061
4062   case WM_USER_Input:
4063     InputEvent(hwnd, message, wParam, lParam);
4064     break;
4065
4066   case WM_ENTERSIZEMOVE:
4067     if (hwnd == hwndMain) {
4068       doingSizing = TRUE;
4069       lastSizing = 0;
4070     }
4071     break;
4072
4073   case WM_SIZING:
4074     if (hwnd == hwndMain) {
4075       lastSizing = wParam;
4076     }
4077     break;
4078
4079   case WM_EXITSIZEMOVE:
4080     if (hwnd == hwndMain) {
4081       RECT client;
4082       doingSizing = FALSE;
4083       InvalidateRect(hwnd, &boardRect, FALSE);
4084       GetClientRect(hwnd, &client);
4085       ResizeBoard(client.right, client.bottom, lastSizing);
4086       lastSizing = 0;
4087     }
4088     break;
4089
4090   case WM_DESTROY: /* message: window being destroyed */
4091     PostQuitMessage(0);
4092     break;
4093
4094   case WM_CLOSE:
4095     if (hwnd == hwndMain) {
4096       ExitEvent(0);
4097     }
4098     break;
4099
4100   default:      /* Passes it on if unprocessed */
4101     return (DefWindowProc(hwnd, message, wParam, lParam));
4102   }
4103   return 0;
4104 }
4105
4106 /*---------------------------------------------------------------------------*\
4107  *
4108  * Misc utility routines
4109  *
4110 \*---------------------------------------------------------------------------*/
4111
4112 /*
4113  * Decent random number generator, at least not as bad as Windows
4114  * standard rand, which returns a value in the range 0 to 0x7fff.
4115  */
4116 unsigned int randstate;
4117
4118 int
4119 myrandom(void)
4120 {
4121   randstate = randstate * 1664525 + 1013904223;
4122   return (int) randstate & 0x7fffffff;
4123 }
4124
4125 void
4126 mysrandom(unsigned int seed)
4127 {
4128   randstate = seed;
4129 }
4130
4131
4132 /* 
4133  * returns TRUE if user selects a different color, FALSE otherwise 
4134  */
4135
4136 BOOL
4137 ChangeColor(HWND hwnd, COLORREF *which)
4138 {
4139   static BOOL firstTime = TRUE;
4140   static DWORD customColors[16];
4141   CHOOSECOLOR cc;
4142   COLORREF newcolor;
4143   int i;
4144   ColorClass ccl;
4145
4146   if (firstTime) {
4147     /* Make initial colors in use available as custom colors */
4148     /* Should we put the compiled-in defaults here instead? */
4149     i = 0;
4150     customColors[i++] = lightSquareColor & 0xffffff;
4151     customColors[i++] = darkSquareColor & 0xffffff;
4152     customColors[i++] = whitePieceColor & 0xffffff;
4153     customColors[i++] = blackPieceColor & 0xffffff;
4154     customColors[i++] = highlightSquareColor & 0xffffff;
4155     customColors[i++] = premoveHighlightColor & 0xffffff;
4156
4157     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
4158       customColors[i++] = textAttribs[ccl].color;
4159     }
4160     while (i < 16) customColors[i++] = RGB(255, 255, 255);
4161     firstTime = FALSE;
4162   }
4163
4164   cc.lStructSize = sizeof(cc);
4165   cc.hwndOwner = hwnd;
4166   cc.hInstance = NULL;
4167   cc.rgbResult = (DWORD) (*which & 0xffffff);
4168   cc.lpCustColors = (LPDWORD) customColors;
4169   cc.Flags = CC_RGBINIT|CC_FULLOPEN;
4170
4171   if (!ChooseColor(&cc)) return FALSE;
4172
4173   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
4174   if (newcolor == *which) return FALSE;
4175   *which = newcolor;
4176   return TRUE;
4177
4178   /*
4179   InitDrawingColors();
4180   InvalidateRect(hwnd, &boardRect, FALSE);
4181   */
4182 }
4183
4184 BOOLEAN
4185 MyLoadSound(MySound *ms)
4186 {
4187   BOOL ok = FALSE;
4188   struct stat st;
4189   FILE *f;
4190
4191   if (ms->data) free(ms->data);
4192   ms->data = NULL;
4193
4194   switch (ms->name[0]) {
4195   case NULLCHAR:
4196     /* Silence */
4197     ok = TRUE;
4198     break;
4199   case '$':
4200     /* System sound from Control Panel.  Don't preload here. */
4201     ok = TRUE;
4202     break;
4203   case '!':
4204     if (ms->name[1] == NULLCHAR) {
4205       /* "!" alone = silence */
4206       ok = TRUE;
4207     } else {
4208       /* Builtin wave resource.  Error if not found. */
4209       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
4210       if (h == NULL) break;
4211       ms->data = (void *)LoadResource(hInst, h);
4212       if (h == NULL) break;
4213       ok = TRUE;
4214     }
4215     break;
4216   default:
4217     /* .wav file.  Error if not found. */
4218     f = fopen(ms->name, "rb");
4219     if (f == NULL) break;
4220     if (fstat(fileno(f), &st) < 0) break;
4221     ms->data = malloc(st.st_size);
4222     if (fread(ms->data, st.st_size, 1, f) < 1) break;
4223     fclose(f);
4224     ok = TRUE;
4225     break;
4226   }
4227   if (!ok) {
4228     char buf[MSG_SIZ];
4229     sprintf(buf, "Error loading sound %s", ms->name);
4230     DisplayError(buf, GetLastError());
4231   }
4232   return ok;
4233 }
4234
4235 BOOLEAN
4236 MyPlaySound(MySound *ms)
4237 {
4238   BOOLEAN ok = FALSE;
4239   switch (ms->name[0]) {
4240   case NULLCHAR:
4241     /* Silence */
4242     ok = TRUE;
4243     break;
4244   case '$':
4245     /* System sound from Control Panel (deprecated feature).
4246        "$" alone or an unset sound name gets default beep (still in use). */
4247     if (ms->name[1]) {
4248       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
4249     }
4250     if (!ok) ok = MessageBeep(MB_OK);
4251     break; 
4252   case '!':
4253     /* Builtin wave resource, or "!" alone for silence */
4254     if (ms->name[1]) {
4255       if (ms->data == NULL) return FALSE;
4256       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4257     } else {
4258       ok = TRUE;
4259     }
4260     break;
4261   default:
4262     /* .wav file.  Error if not found. */
4263     if (ms->data == NULL) return FALSE;
4264     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4265     break;
4266   }
4267   /* Don't print an error: this can happen innocently if the sound driver
4268      is busy; for instance, if another instance of WinBoard is playing
4269      a sound at about the same time. */
4270 #if 0
4271   if (!ok) {
4272     char buf[MSG_SIZ];
4273     sprintf(buf, "Error playing sound %s", ms->name);
4274     DisplayError(buf, GetLastError());
4275   }
4276 #endif
4277   return ok;
4278 }
4279
4280
4281 LRESULT CALLBACK
4282 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4283 {
4284   BOOL ok;
4285   OPENFILENAME *ofn;
4286   static UINT *number; /* gross that this is static */
4287
4288   switch (message) {
4289   case WM_INITDIALOG: /* message: initialize dialog box */
4290     /* Center the dialog over the application window */
4291     ofn = (OPENFILENAME *) lParam;
4292     if (ofn->Flags & OFN_ENABLETEMPLATE) {
4293       number = (UINT *) ofn->lCustData;
4294       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
4295     } else {
4296       number = NULL;
4297     }
4298     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4299     return FALSE;  /* Allow for further processing */
4300
4301   case WM_COMMAND:
4302     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
4303       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
4304     }
4305     return FALSE;  /* Allow for further processing */
4306   }
4307   return FALSE;
4308 }
4309
4310 UINT APIENTRY
4311 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
4312 {
4313   static UINT *number;
4314   OPENFILENAME *ofname;
4315   OFNOTIFY *ofnot;
4316   switch (uiMsg) {
4317   case WM_INITDIALOG:
4318     ofname = (OPENFILENAME *)lParam;
4319     number = (UINT *)(ofname->lCustData);
4320     break;
4321   case WM_NOTIFY:
4322     ofnot = (OFNOTIFY *)lParam;
4323     if (ofnot->hdr.code == CDN_FILEOK) {
4324       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
4325     }
4326     break;
4327   }
4328   return 0;
4329 }
4330
4331
4332 FILE *
4333 OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,
4334                char *nameFilt, char *dlgTitle, UINT *number,
4335                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
4336 {
4337   OPENFILENAME openFileName;
4338   char buf1[MSG_SIZ];
4339   FILE *f;
4340
4341   if (fileName == NULL) fileName = buf1;
4342   if (defName == NULL) {
4343     strcpy(fileName, "*.");
4344     strcat(fileName, defExt);
4345   } else {
4346     strcpy(fileName, defName);
4347   }
4348   if (fileTitle) strcpy(fileTitle, "");
4349   if (number) *number = 0;
4350
4351   openFileName.lStructSize       = sizeof(OPENFILENAME);
4352   openFileName.hwndOwner         = hwnd;
4353   openFileName.hInstance         = (HANDLE) hInst;
4354   openFileName.lpstrFilter       = nameFilt;
4355   openFileName.lpstrCustomFilter = (LPSTR) NULL;
4356   openFileName.nMaxCustFilter    = 0L;
4357   openFileName.nFilterIndex      = 1L;
4358   openFileName.lpstrFile         = fileName;
4359   openFileName.nMaxFile          = MSG_SIZ;
4360   openFileName.lpstrFileTitle    = fileTitle;
4361   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;
4362   openFileName.lpstrInitialDir   = NULL;
4363   openFileName.lpstrTitle        = dlgTitle;
4364   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY 
4365     | (write ? 0 : OFN_FILEMUSTEXIST) 
4366     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
4367     | (oldDialog ? 0 : OFN_EXPLORER);
4368   openFileName.nFileOffset       = 0;
4369   openFileName.nFileExtension    = 0;
4370   openFileName.lpstrDefExt       = defExt;
4371   openFileName.lCustData         = (LONG) number;
4372   openFileName.lpfnHook          = oldDialog ?
4373     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
4374   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
4375
4376   if (write ? GetSaveFileName(&openFileName) : 
4377               GetOpenFileName(&openFileName)) {
4378     /* open the file */
4379     f = fopen(openFileName.lpstrFile, write ? "a" : "rb");
4380     if (f == NULL) {
4381       MessageBox(hwnd, "File open failed", NULL,
4382                  MB_OK|MB_ICONEXCLAMATION);
4383       return NULL;
4384     }
4385   } else {
4386     int err = CommDlgExtendedError();
4387     if (err != 0) DisplayError("Internal error in file dialog box", err);
4388     return FALSE;
4389   }
4390   return f;
4391 }
4392
4393
4394
4395 VOID APIENTRY
4396 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
4397 {
4398   HMENU hmenuTrackPopup;        /* floating pop-up menu  */
4399
4400   /*
4401    * Get the first pop-up menu in the menu template. This is the
4402    * menu that TrackPopupMenu displays.
4403    */
4404   hmenuTrackPopup = GetSubMenu(hmenu, 0);
4405
4406   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
4407
4408   /*
4409    * TrackPopup uses screen coordinates, so convert the
4410    * coordinates of the mouse click to screen coordinates.
4411    */
4412   ClientToScreen(hwnd, (LPPOINT) &pt);
4413
4414   /* Draw and track the floating pop-up menu. */
4415   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
4416                  pt.x, pt.y, 0, hwnd, NULL);
4417
4418   /* Destroy the menu.*/
4419   DestroyMenu(hmenu);
4420 }
4421    
4422 typedef struct {
4423   HWND hDlg, hText;
4424   int sizeX, sizeY, newSizeX, newSizeY;
4425   HDWP hdwp;
4426 } ResizeEditPlusButtonsClosure;
4427
4428 BOOL CALLBACK
4429 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
4430 {
4431   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
4432   RECT rect;
4433   POINT pt;
4434
4435   if (hChild == cl->hText) return TRUE;
4436   GetWindowRect(hChild, &rect); /* gives screen coords */
4437   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
4438   pt.y = rect.top + cl->newSizeY - cl->sizeY;
4439   ScreenToClient(cl->hDlg, &pt);
4440   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, 
4441     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
4442   return TRUE;
4443 }
4444
4445 /* Resize a dialog that has a (rich) edit field filling most of
4446    the top, with a row of buttons below */
4447 VOID
4448 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
4449 {
4450   RECT rectText;
4451   int newTextHeight, newTextWidth;
4452   ResizeEditPlusButtonsClosure cl;
4453   
4454   /*if (IsIconic(hDlg)) return;*/
4455   if (newSizeX == sizeX && newSizeY == sizeY) return;
4456   
4457   cl.hdwp = BeginDeferWindowPos(8);
4458
4459   GetWindowRect(hText, &rectText); /* gives screen coords */
4460   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
4461   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
4462   if (newTextHeight < 0) {
4463     newSizeY += -newTextHeight;
4464     newTextHeight = 0;
4465   }
4466   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, 
4467     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
4468
4469   cl.hDlg = hDlg;
4470   cl.hText = hText;
4471   cl.sizeX = sizeX;
4472   cl.sizeY = sizeY;
4473   cl.newSizeX = newSizeX;
4474   cl.newSizeY = newSizeY;
4475   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
4476
4477   EndDeferWindowPos(cl.hdwp);
4478 }
4479
4480 /* Center one window over another */
4481 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
4482 {
4483     RECT    rChild, rParent;
4484     int     wChild, hChild, wParent, hParent;
4485     int     wScreen, hScreen, xNew, yNew;
4486     HDC     hdc;
4487
4488     /* Get the Height and Width of the child window */
4489     GetWindowRect (hwndChild, &rChild);
4490     wChild = rChild.right - rChild.left;
4491     hChild = rChild.bottom - rChild.top;
4492
4493     /* Get the Height and Width of the parent window */
4494     GetWindowRect (hwndParent, &rParent);
4495     wParent = rParent.right - rParent.left;
4496     hParent = rParent.bottom - rParent.top;
4497
4498     /* Get the display limits */
4499     hdc = GetDC (hwndChild);
4500     wScreen = GetDeviceCaps (hdc, HORZRES);
4501     hScreen = GetDeviceCaps (hdc, VERTRES);
4502     ReleaseDC(hwndChild, hdc);
4503
4504     /* Calculate new X position, then adjust for screen */
4505     xNew = rParent.left + ((wParent - wChild) /2);
4506     if (xNew < 0) {
4507         xNew = 0;
4508     } else if ((xNew+wChild) > wScreen) {
4509         xNew = wScreen - wChild;
4510     }
4511
4512     /* Calculate new Y position, then adjust for screen */
4513     yNew = rParent.top  + ((hParent - hChild) /2);
4514     if (yNew < 0) {
4515         yNew = 0;
4516     } else if ((yNew+hChild) > hScreen) {
4517         yNew = hScreen - hChild;
4518     }
4519
4520     /* Set it, and return */
4521     return SetWindowPos (hwndChild, NULL,
4522                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4523 }
4524
4525 /*---------------------------------------------------------------------------*\
4526  *
4527  * Startup Dialog functions
4528  *
4529 \*---------------------------------------------------------------------------*/
4530 void
4531 InitComboStrings(HANDLE hwndCombo, char **cd)
4532 {
4533   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4534
4535   while (*cd != NULL) {
4536     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
4537     cd++;
4538   }
4539 }
4540
4541 void
4542 InitComboStringsFromOption(HANDLE hwndCombo, char *str)
4543 {
4544   char buf1[ARG_MAX];
4545   int len;
4546
4547   if (str[0] == '@') {
4548     FILE* f = fopen(str + 1, "r");
4549     if (f == NULL) {
4550       DisplayFatalError(str + 1, errno, 2);
4551       return;
4552     }
4553     len = fread(buf1, 1, sizeof(buf1)-1, f);
4554     fclose(f);
4555     buf1[len] = NULLCHAR;
4556     str = buf1;
4557   }
4558
4559   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
4560
4561   for (;;) {
4562     char buf[MSG_SIZ];
4563     char *end = strchr(str, '\n');
4564     if (end == NULL) return;
4565     memcpy(buf, str, end - str);
4566     buf[end - str] = NULLCHAR;
4567     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
4568     str = end + 1;
4569   }
4570 }
4571
4572 void
4573 SetStartupDialogEnables(HWND hDlg)
4574 {
4575   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
4576     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4577     appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));
4578   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
4579     IsDlgButtonChecked(hDlg, OPT_ChessEngine));
4580   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
4581     IsDlgButtonChecked(hDlg, OPT_ChessServer));
4582   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
4583     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
4584   EnableWindow(GetDlgItem(hDlg, IDOK),
4585     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
4586     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
4587     IsDlgButtonChecked(hDlg, OPT_View));
4588 }
4589
4590 char *
4591 QuoteForFilename(char *filename)
4592 {
4593   int dquote, space;
4594   dquote = strchr(filename, '"') != NULL;
4595   space = strchr(filename, ' ') != NULL;
4596   if (dquote || space) {
4597     if (dquote) {
4598       return "'";
4599     } else {
4600       return "\"";
4601     }
4602   } else {
4603     return "";
4604   }
4605 }
4606
4607 VOID
4608 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
4609 {
4610   char buf[MSG_SIZ];
4611   char *q;
4612
4613   InitComboStringsFromOption(hwndCombo, nthnames);
4614   q = QuoteForFilename(nthcp);
4615   sprintf(buf, "%s%s%s", q, nthcp, q);
4616   if (*nthdir != NULLCHAR) {
4617     q = QuoteForFilename(nthdir);
4618     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
4619   }
4620   if (*nthcp == NULLCHAR) {
4621     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4622   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4623     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4624     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4625   }
4626 }
4627
4628 LRESULT CALLBACK
4629 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4630 {
4631   char buf[MSG_SIZ];
4632   HANDLE hwndCombo;
4633   char *p;
4634
4635   switch (message) {
4636   case WM_INITDIALOG:
4637     /* Center the dialog */
4638     CenterWindow (hDlg, GetDesktopWindow());
4639     /* Initialize the dialog items */
4640     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
4641                   appData.firstChessProgram, "fd", appData.firstDirectory,
4642                   firstChessProgramNames);
4643     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
4644                   appData.secondChessProgram, "sd", appData.secondDirectory,
4645                   secondChessProgramNames);
4646     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
4647     InitComboStringsFromOption(hwndCombo, icsNames);    
4648     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
4649     if (*appData.icsHelper != NULLCHAR) {
4650       char *q = QuoteForFilename(appData.icsHelper);
4651       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
4652     }
4653     if (*appData.icsHost == NULLCHAR) {
4654       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
4655       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
4656     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
4657       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
4658       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
4659     }
4660     if (chessProgram) {
4661       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
4662     } else if (appData.icsActive) {
4663       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
4664     } else if (appData.noChessProgram) {
4665       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
4666     }
4667     SetStartupDialogEnables(hDlg);
4668     return TRUE;
4669
4670   case WM_COMMAND:
4671     switch (LOWORD(wParam)) {
4672     case IDOK:
4673       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
4674         strcpy(buf, "/fcp=");
4675         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4676         p = buf;
4677         ParseArgs(StringGet, &p);
4678         strcpy(buf, "/scp=");
4679         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4680         p = buf;
4681         ParseArgs(StringGet, &p);
4682         appData.noChessProgram = FALSE;
4683         appData.icsActive = FALSE;
4684       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
4685         strcpy(buf, "/ics /icshost=");
4686         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4687         p = buf;
4688         ParseArgs(StringGet, &p);
4689         if (appData.zippyPlay) {
4690           strcpy(buf, "/fcp=");
4691           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
4692           p = buf;
4693           ParseArgs(StringGet, &p);
4694         }
4695       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
4696         appData.noChessProgram = TRUE;
4697         appData.icsActive = FALSE;
4698       } else {
4699         MessageBox(hDlg, "Choose an option, or cancel to exit",
4700                    "Option Error", MB_OK|MB_ICONEXCLAMATION);
4701         return TRUE;
4702       }
4703       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
4704         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
4705         p = buf;
4706         ParseArgs(StringGet, &p);
4707       }
4708       EndDialog(hDlg, TRUE);
4709       return TRUE;
4710
4711     case IDCANCEL:
4712       ExitEvent(0);
4713       return TRUE;
4714
4715     case IDM_HELPCONTENTS:
4716       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
4717         MessageBox (GetFocus(),
4718                     "Unable to activate help",
4719                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
4720       }
4721       break;
4722
4723     default:
4724       SetStartupDialogEnables(hDlg);
4725       break;
4726     }
4727     break;
4728   }
4729   return FALSE;
4730 }
4731
4732 /*---------------------------------------------------------------------------*\
4733  *
4734  * About box dialog functions
4735  *
4736 \*---------------------------------------------------------------------------*/
4737
4738 /* Process messages for "About" dialog box */
4739 LRESULT CALLBACK
4740 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4741 {
4742   switch (message) {
4743   case WM_INITDIALOG: /* message: initialize dialog box */
4744     /* Center the dialog over the application window */
4745     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
4746     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
4747     return (TRUE);
4748
4749   case WM_COMMAND: /* message: received a command */
4750     if (LOWORD(wParam) == IDOK /* "OK" box selected? */
4751         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
4752       EndDialog(hDlg, TRUE); /* Exit the dialog */
4753       return (TRUE);
4754     }
4755     break;
4756   }
4757   return (FALSE);
4758 }
4759
4760 /*---------------------------------------------------------------------------*\
4761  *
4762  * Comment Dialog functions
4763  *
4764 \*---------------------------------------------------------------------------*/
4765
4766 LRESULT CALLBACK
4767 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4768 {
4769   static HANDLE hwndText = NULL;
4770   int len, newSizeX, newSizeY, flags;
4771   static int sizeX, sizeY;
4772   char *str;
4773   RECT rect;
4774   MINMAXINFO *mmi;
4775
4776   switch (message) {
4777   case WM_INITDIALOG: /* message: initialize dialog box */
4778     /* Initialize the dialog items */
4779     hwndText = GetDlgItem(hDlg, OPT_CommentText);
4780     SetDlgItemText(hDlg, OPT_CommentText, commentText);
4781     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
4782     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
4783     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
4784     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
4785     SetWindowText(hDlg, commentTitle);
4786     if (editComment) {
4787       SetFocus(hwndText);
4788     } else {
4789       SetFocus(GetDlgItem(hDlg, IDOK));
4790     }
4791     SendMessage(GetDlgItem(hDlg, OPT_CommentText),
4792                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
4793                 MAKELPARAM(FALSE, 0));
4794     /* Size and position the dialog */
4795     if (!commentDialog) {
4796       commentDialog = hDlg;
4797       flags = SWP_NOZORDER;
4798       GetClientRect(hDlg, &rect);
4799       sizeX = rect.right;
4800       sizeY = rect.bottom;
4801       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&
4802           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {
4803         WINDOWPLACEMENT wp;
4804         EnsureOnScreen(&commentX, &commentY);
4805         wp.length = sizeof(WINDOWPLACEMENT);
4806         wp.flags = 0;
4807         wp.showCmd = SW_SHOW;
4808         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
4809         wp.rcNormalPosition.left = commentX;
4810         wp.rcNormalPosition.right = commentX + commentW;
4811         wp.rcNormalPosition.top = commentY;
4812         wp.rcNormalPosition.bottom = commentY + commentH;
4813         SetWindowPlacement(hDlg, &wp);
4814
4815         GetClientRect(hDlg, &rect);
4816         newSizeX = rect.right;
4817         newSizeY = rect.bottom;
4818         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
4819                               newSizeX, newSizeY);
4820         sizeX = newSizeX;
4821         sizeY = newSizeY;
4822       }
4823     }
4824     return FALSE;
4825
4826   case WM_COMMAND: /* message: received a command */
4827     switch (LOWORD(wParam)) {
4828     case IDOK:
4829       if (editComment) {
4830         char *p, *q;
4831         /* Read changed options from the dialog box */
4832         hwndText = GetDlgItem(hDlg, OPT_CommentText);
4833         len = GetWindowTextLength(hwndText);
4834         str = (char *) malloc(len + 1);
4835         GetWindowText(hwndText, str, len + 1);
4836         p = q = str;
4837         while (*q) {
4838           if (*q == '\r')
4839             q++;
4840           else
4841             *p++ = *q++;
4842         }
4843         *p = NULLCHAR;
4844         ReplaceComment(commentIndex, str);
4845         free(str);
4846       }
4847       CommentPopDown();
4848       return TRUE;
4849
4850     case IDCANCEL:
4851     case OPT_CancelComment:
4852       CommentPopDown();
4853       return TRUE;
4854
4855     case OPT_ClearComment:
4856       SetDlgItemText(hDlg, OPT_CommentText, "");
4857       break;
4858
4859     case OPT_EditComment:
4860       EditCommentEvent();
4861       return TRUE;
4862
4863     default:
4864       break;
4865     }
4866     break;
4867
4868   case WM_SIZE:
4869     newSizeX = LOWORD(lParam);
4870     newSizeY = HIWORD(lParam);
4871     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
4872     sizeX = newSizeX;
4873     sizeY = newSizeY;
4874     break;
4875
4876   case WM_GETMINMAXINFO:
4877     /* Prevent resizing window too small */
4878     mmi = (MINMAXINFO *) lParam;
4879     mmi->ptMinTrackSize.x = 100;
4880     mmi->ptMinTrackSize.y = 100;
4881     break;
4882   }
4883   return FALSE;
4884 }
4885
4886 VOID
4887 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
4888 {
4889   FARPROC lpProc;
4890   char *p, *q;
4891
4892   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
4893
4894   if (str == NULL) str = "";
4895   p = (char *) malloc(2 * strlen(str) + 2);
4896   q = p;
4897   while (*str) {
4898     if (*str == '\n') *q++ = '\r';
4899     *q++ = *str++;
4900   }
4901   *q = NULLCHAR;
4902   if (commentText != NULL) free(commentText);
4903
4904   commentIndex = index;
4905   commentTitle = title;
4906   commentText = p;
4907   editComment = edit;
4908
4909   if (commentDialog) {
4910     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
4911     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);
4912   } else {
4913     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
4914     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
4915                  hwndMain, (DLGPROC)lpProc);
4916     FreeProcInstance(lpProc);
4917   }
4918   commentDialogUp = TRUE;
4919 }
4920
4921
4922 /*---------------------------------------------------------------------------*\
4923  *
4924  * Type-in move dialog functions
4925  * 
4926 \*---------------------------------------------------------------------------*/
4927
4928 LRESULT CALLBACK
4929 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
4930 {
4931   char move[MSG_SIZ];
4932   HWND hInput;
4933   ChessMove moveType;
4934   int fromX, fromY, toX, toY;
4935   char promoChar;
4936
4937   switch (message) {
4938   case WM_INITDIALOG:
4939     move[0] = (char) lParam;
4940     move[1] = NULLCHAR;
4941     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
4942     hInput = GetDlgItem(hDlg, OPT_Move);
4943     SetWindowText(hInput, move);
4944     SetFocus(hInput);
4945     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
4946     return FALSE;
4947
4948   case WM_COMMAND:
4949     switch (LOWORD(wParam)) {
4950     case IDOK:
4951       if (gameMode != EditGame && currentMove != forwardMostMove && 
4952         gameMode != Training) {
4953         DisplayMoveError("Displayed move is not current");
4954       } else {
4955         GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
4956         if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, 
4957           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
4958           if (gameMode != Training)
4959               forwardMostMove = currentMove;
4960           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     
4961         } else {
4962           DisplayMoveError("Could not parse move");
4963         }
4964       }
4965       EndDialog(hDlg, TRUE);
4966       return TRUE;
4967     case IDCANCEL:
4968       EndDialog(hDlg, FALSE);
4969       return TRUE;
4970     default:
4971       break;
4972     }
4973     break;
4974   }
4975   return FALSE;
4976 }
4977
4978 VOID
4979 PopUpMoveDialog(char firstchar)
4980 {
4981     FARPROC lpProc;
4982     
4983     if ((gameMode == BeginningOfGame && !appData.icsActive) || 
4984         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
4985         gameMode == AnalyzeMode || gameMode == EditGame || 
4986         gameMode == EditPosition || gameMode == IcsExamining ||
4987         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
4988         gameMode == Training) {
4989       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
4990       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
4991         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
4992       FreeProcInstance(lpProc);
4993     }
4994 }
4995
4996 /*---------------------------------------------------------------------------*\
4997  *
4998  *  Error dialogs
4999  * 
5000 \*---------------------------------------------------------------------------*/
5001
5002 /* Nonmodal error box */
5003 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,
5004                              WPARAM wParam, LPARAM lParam);
5005
5006 VOID
5007 ErrorPopUp(char *title, char *content)
5008 {
5009   FARPROC lpProc;
5010   char *p, *q;
5011   BOOLEAN modal = hwndMain == NULL;
5012
5013   p = content;
5014   q = errorMessage;
5015   while (*p) {
5016     if (*p == '\n') {
5017       if (modal) {
5018         *q++ = ' ';
5019         p++;
5020       } else {
5021         *q++ = '\r';
5022         *q++ = *p++;
5023       }
5024     } else {
5025       *q++ = *p++;
5026     }
5027   }
5028   *q = NULLCHAR;
5029   strncpy(errorTitle, title, sizeof(errorTitle));
5030   errorTitle[sizeof(errorTitle) - 1] = '\0';
5031   
5032   if (modal) {
5033     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);
5034   } else {
5035     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
5036     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
5037                  hwndMain, (DLGPROC)lpProc);
5038     FreeProcInstance(lpProc);
5039   }
5040 }
5041
5042 VOID
5043 ErrorPopDown()
5044 {
5045   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
5046   if (errorDialog == NULL) return;
5047   DestroyWindow(errorDialog);
5048   errorDialog = NULL;
5049 }
5050
5051 LRESULT CALLBACK
5052 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5053 {
5054   HANDLE hwndText;
5055   RECT rChild;
5056
5057   switch (message) {
5058   case WM_INITDIALOG:
5059     GetWindowRect(hDlg, &rChild);
5060     SetWindowPos(hDlg, NULL, rChild.left,
5061       rChild.top + boardRect.top - (rChild.bottom - rChild.top), 
5062       0, 0, SWP_NOZORDER|SWP_NOSIZE);
5063     errorDialog = hDlg;
5064     SetWindowText(hDlg, errorTitle);
5065     hwndText = GetDlgItem(hDlg, OPT_ErrorText);
5066     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
5067     return FALSE;
5068
5069   case WM_COMMAND:
5070     switch (LOWORD(wParam)) {
5071     case IDOK:
5072     case IDCANCEL:
5073       if (errorDialog == hDlg) errorDialog = NULL;
5074       DestroyWindow(hDlg);
5075       return TRUE;
5076
5077     default:
5078       break;
5079     }
5080     break;
5081   }
5082   return FALSE;
5083 }
5084
5085 /*---------------------------------------------------------------------------*\
5086  *
5087  *  Ics Interaction console functions
5088  *
5089 \*---------------------------------------------------------------------------*/
5090
5091 #define HISTORY_SIZE 64
5092 static char *history[HISTORY_SIZE];
5093 int histIn = 0, histP = 0;
5094
5095 VOID
5096 SaveInHistory(char *cmd)
5097 {
5098   if (history[histIn] != NULL) {
5099     free(history[histIn]);
5100     history[histIn] = NULL;
5101   }
5102   if (*cmd == NULLCHAR) return;
5103   history[histIn] = StrSave(cmd);
5104   histIn = (histIn + 1) % HISTORY_SIZE;
5105   if (history[histIn] != NULL) {
5106     free(history[histIn]);
5107     history[histIn] = NULL;
5108   }
5109   histP = histIn;
5110 }
5111
5112 char *
5113 PrevInHistory(char *cmd)
5114 {
5115   int newhp;
5116   if (histP == histIn) {
5117     if (history[histIn] != NULL) free(history[histIn]);
5118     history[histIn] = StrSave(cmd);
5119   }
5120   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
5121   if (newhp == histIn || history[newhp] == NULL) return NULL;
5122   histP = newhp;
5123   return history[histP];
5124 }
5125
5126 char *
5127 NextInHistory()
5128 {
5129   if (histP == histIn) return NULL;
5130   histP = (histP + 1) % HISTORY_SIZE;
5131   return history[histP];
5132 }
5133
5134 typedef struct {
5135   char *item;
5136   char *command;
5137   BOOLEAN getname;
5138   BOOLEAN immediate;
5139 } IcsTextMenuEntry;
5140 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
5141 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];
5142
5143 void
5144 ParseIcsTextMenu(char *icsTextMenuString)
5145 {
5146   int flags = 0;
5147   IcsTextMenuEntry *e = icsTextMenuEntry;
5148   char *p = icsTextMenuString;
5149   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5150     free(e->item);
5151     e->item = NULL;
5152     if (e->command != NULL) {
5153       free(e->command);
5154       e->command = NULL;
5155     }
5156     e++;
5157   }
5158   e = icsTextMenuEntry;
5159   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
5160     if (*p == ';' || *p == '\n') {
5161       e->item = strdup("-");
5162       e->command = NULL;
5163       p++;
5164     } else if (*p == '-') {
5165       e->item = strdup("-");
5166       e->command = NULL;
5167       p++;
5168       if (*p) p++;
5169     } else {
5170       char *q, *r, *s, *t;
5171       char c;
5172       q = strchr(p, ',');
5173       if (q == NULL) break;
5174       *q = NULLCHAR;
5175       r = strchr(q + 1, ',');
5176       if (r == NULL) break;
5177       *r = NULLCHAR;
5178       s = strchr(r + 1, ',');
5179       if (s == NULL) break;
5180       *s = NULLCHAR;
5181       c = ';';
5182       t = strchr(s + 1, c);
5183       if (t == NULL) {
5184         c = '\n';
5185         t = strchr(s + 1, c);
5186       }
5187       if (t != NULL) *t = NULLCHAR;
5188       e->item = strdup(p);
5189       e->command = strdup(q + 1);
5190       e->getname = *(r + 1) != '0';
5191       e->immediate = *(s + 1) != '0';
5192       *q = ',';
5193       *r = ',';
5194       *s = ',';
5195       if (t == NULL) break;
5196       *t = c;
5197       p = t + 1;
5198     }
5199     e++;
5200   } 
5201 }
5202
5203 HMENU
5204 LoadIcsTextMenu(IcsTextMenuEntry *e)
5205 {
5206   HMENU hmenu, h;
5207   int i = 0;
5208   hmenu = LoadMenu(hInst, "TextMenu");
5209   h = GetSubMenu(hmenu, 0);
5210   while (e->item) {
5211     if (strcmp(e->item, "-") == 0) {
5212       AppendMenu(h, MF_SEPARATOR, 0, 0);
5213     } else {
5214       if (e->item[0] == '|') {
5215         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
5216                    IDM_CommandX + i, &e->item[1]);
5217       } else {
5218         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
5219       }
5220     }
5221     e++;
5222     i++;
5223   } 
5224   return hmenu;
5225 }
5226
5227 WNDPROC consoleTextWindowProc;
5228
5229 void
5230 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
5231 {
5232   char buf[MSG_SIZ], name[MSG_SIZ];
5233   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5234   CHARRANGE sel;
5235
5236   if (!getname) {
5237     SetWindowText(hInput, command);
5238     if (immediate) {
5239       SendMessage(hInput, WM_CHAR, '\r', 0);
5240     } else {
5241       sel.cpMin = 999999;
5242       sel.cpMax = 999999;
5243       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5244       SetFocus(hInput);
5245     }
5246     return;
5247   }    
5248   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5249   if (sel.cpMin == sel.cpMax) {
5250     /* Expand to surrounding word */
5251     TEXTRANGE tr;
5252     do {
5253       tr.chrg.cpMax = sel.cpMin;
5254       tr.chrg.cpMin = --sel.cpMin;
5255       if (sel.cpMin < 0) break;
5256       tr.lpstrText = name;
5257       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5258     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5259     sel.cpMin++;
5260
5261     do {
5262       tr.chrg.cpMin = sel.cpMax;
5263       tr.chrg.cpMax = ++sel.cpMax;
5264       tr.lpstrText = name;
5265       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
5266     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
5267     sel.cpMax--;
5268
5269     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5270       MessageBeep(MB_ICONEXCLAMATION);
5271       return;
5272     }
5273     tr.chrg = sel;
5274     tr.lpstrText = name;
5275     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
5276   } else {
5277     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
5278       MessageBeep(MB_ICONEXCLAMATION);
5279       return;
5280     }
5281     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
5282   }
5283   if (immediate) {
5284     sprintf(buf, "%s %s", command, name);
5285     SetWindowText(hInput, buf);
5286     SendMessage(hInput, WM_CHAR, '\r', 0);
5287   } else {
5288     sprintf(buf, "%s %s ", command, name); /* trailing space */
5289     SetWindowText(hInput, buf);
5290     sel.cpMin = 999999;
5291     sel.cpMax = 999999;
5292     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
5293     SetFocus(hInput);
5294   }
5295 }
5296
5297 LRESULT CALLBACK 
5298 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5299 {
5300   HWND hInput;
5301   CHARRANGE sel;
5302
5303   switch (message) {
5304   case WM_KEYDOWN:
5305     if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5306     switch (wParam) {
5307     case VK_PRIOR:
5308       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
5309       return 0;
5310     case VK_NEXT:
5311       sel.cpMin = 999999;
5312       sel.cpMax = 999999;
5313       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5314       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
5315       return 0;
5316     }
5317     break;
5318   case WM_CHAR:
5319     if (wParam == '\t') {
5320       if (GetKeyState(VK_SHIFT) < 0) {
5321         /* shifted */
5322         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5323         if (buttonDesc[0].hwnd) {
5324           SetFocus(buttonDesc[0].hwnd);
5325         } else {
5326           SetFocus(hwndMain);
5327         }
5328       } else {
5329         /* unshifted */
5330         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
5331       }
5332     } else {
5333       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5334       SetFocus(hInput);
5335       SendMessage(hInput, message, wParam, lParam);
5336     }
5337     return 0;
5338   case WM_PASTE:
5339     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5340     SetFocus(hInput);
5341     return SendMessage(hInput, message, wParam, lParam);
5342   case WM_MBUTTONDOWN:
5343     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5344   case WM_RBUTTONDOWN:
5345     if (!(GetKeyState(VK_SHIFT) & ~1)) {
5346       /* Move selection here if it was empty */
5347       POINT pt;
5348       pt.x = LOWORD(lParam);
5349       pt.y = HIWORD(lParam);
5350       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5351       if (sel.cpMin == sel.cpMax) {
5352         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
5353         sel.cpMax = sel.cpMin;
5354         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5355       }
5356       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
5357     }
5358     return 0;
5359   case WM_RBUTTONUP:
5360     if (GetKeyState(VK_SHIFT) & ~1) {
5361       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, 
5362         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5363     } else {
5364       POINT pt;
5365       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
5366       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5367       if (sel.cpMin == sel.cpMax) {
5368         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5369         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);
5370       }
5371       if (!IsClipboardFormatAvailable(CF_TEXT)) {
5372         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5373       }
5374       pt.x = LOWORD(lParam);
5375       pt.y = HIWORD(lParam);
5376       MenuPopup(hwnd, pt, hmenu, -1);
5377     }
5378     return 0;
5379   case WM_COMMAND:
5380     switch (LOWORD(wParam)) {
5381     case IDM_QuickPaste:
5382       {
5383         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5384         if (sel.cpMin == sel.cpMax) {
5385           MessageBeep(MB_ICONEXCLAMATION);
5386           return 0;
5387         }
5388         SendMessage(hwnd, WM_COPY, 0, 0);
5389         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
5390         SendMessage(hInput, WM_PASTE, 0, 0);
5391         SetFocus(hInput);
5392         return 0;
5393       }
5394     case IDM_Cut:
5395       SendMessage(hwnd, WM_CUT, 0, 0);
5396       return 0;
5397     case IDM_Paste:
5398       SendMessage(hwnd, WM_PASTE, 0, 0);
5399       return 0;
5400     case IDM_Copy:
5401       SendMessage(hwnd, WM_COPY, 0, 0);
5402       return 0;
5403     default:
5404       {
5405         int i = LOWORD(wParam) - IDM_CommandX;
5406         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
5407             icsTextMenuEntry[i].command != NULL) {
5408           CommandX(hwnd, icsTextMenuEntry[i].command,
5409                    icsTextMenuEntry[i].getname,
5410                    icsTextMenuEntry[i].immediate);
5411           return 0;
5412         }
5413       }
5414       break;
5415     }
5416     break;
5417   }
5418   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
5419 }
5420
5421 WNDPROC consoleInputWindowProc;
5422
5423 LRESULT CALLBACK
5424 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5425 {
5426   char buf[MSG_SIZ];
5427   char *p;
5428   static BOOL sendNextChar = FALSE;
5429   static BOOL quoteNextChar = FALSE;
5430   InputSource *is = consoleInputSource;
5431   CHARFORMAT cf;
5432   CHARRANGE sel;
5433
5434   switch (message) {
5435   case WM_CHAR:
5436     if (!appData.localLineEditing || sendNextChar) {
5437       is->buf[0] = (CHAR) wParam;
5438       is->count = 1;
5439       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5440       sendNextChar = FALSE;
5441       return 0;
5442     }
5443     if (quoteNextChar) {
5444       buf[0] = (char) wParam;
5445       buf[1] = NULLCHAR;
5446       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
5447       quoteNextChar = FALSE;
5448       return 0;
5449     }
5450     switch (wParam) {
5451     case '\r':   /* Enter key */
5452       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     
5453       if (consoleEcho) SaveInHistory(is->buf);
5454       is->buf[is->count++] = '\n';
5455       is->buf[is->count] = NULLCHAR;
5456       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5457       if (consoleEcho) {
5458         ConsoleOutput(is->buf, is->count, TRUE);
5459       } else if (appData.localLineEditing) {
5460         ConsoleOutput("\n", 1, TRUE);
5461       }
5462       /* fall thru */
5463     case '\033': /* Escape key */
5464       SetWindowText(hwnd, "");
5465       cf.cbSize = sizeof(CHARFORMAT);
5466       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
5467       if (consoleEcho) {
5468         cf.crTextColor = textAttribs[ColorNormal].color;
5469       } else {
5470         cf.crTextColor = COLOR_ECHOOFF;
5471       }
5472       cf.dwEffects = textAttribs[ColorNormal].effects;
5473       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
5474       return 0;
5475     case '\t':   /* Tab key */
5476       if (GetKeyState(VK_SHIFT) < 0) {
5477         /* shifted */
5478         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
5479       } else {
5480         /* unshifted */
5481         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
5482         if (buttonDesc[0].hwnd) {
5483           SetFocus(buttonDesc[0].hwnd);
5484         } else {
5485           SetFocus(hwndMain);
5486         }
5487       }
5488       return 0;
5489     case '\023': /* Ctrl+S */
5490       sendNextChar = TRUE;
5491       return 0;
5492     case '\021': /* Ctrl+Q */
5493       quoteNextChar = TRUE;
5494       return 0;
5495     default:
5496       break;
5497     }
5498     break;
5499   case WM_KEYDOWN:
5500     switch (wParam) {
5501     case VK_UP:
5502       GetWindowText(hwnd, buf, MSG_SIZ);
5503       p = PrevInHistory(buf);
5504       if (p != NULL) {
5505         SetWindowText(hwnd, p);
5506         sel.cpMin = 999999;
5507         sel.cpMax = 999999;
5508         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5509         return 0;
5510       }
5511       break;
5512     case VK_DOWN:
5513       p = NextInHistory();
5514       if (p != NULL) {
5515         SetWindowText(hwnd, p);
5516         sel.cpMin = 999999;
5517         sel.cpMax = 999999;
5518         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5519         return 0;
5520       }
5521       break;
5522     case VK_HOME:
5523     case VK_END:
5524       if (!(GetKeyState(VK_CONTROL) & ~1)) break;
5525       /* fall thru */
5526     case VK_PRIOR:
5527     case VK_NEXT:
5528       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
5529       return 0;
5530     }
5531     break;
5532   case WM_MBUTTONDOWN:
5533     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, 
5534       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5535     break;
5536   case WM_RBUTTONUP:
5537     if (GetKeyState(VK_SHIFT) & ~1) {
5538       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, 
5539         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
5540     } else {
5541       POINT pt;
5542       HMENU hmenu;
5543       hmenu = LoadMenu(hInst, "InputMenu");
5544       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
5545       if (sel.cpMin == sel.cpMax) {
5546         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
5547         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
5548       }
5549       if (!IsClipboardFormatAvailable(CF_TEXT)) {
5550         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
5551       }
5552       pt.x = LOWORD(lParam);
5553       pt.y = HIWORD(lParam);
5554       MenuPopup(hwnd, pt, hmenu, -1);
5555     }
5556     return 0;
5557   case WM_COMMAND:
5558     switch (LOWORD(wParam)) { 
5559     case IDM_Undo:
5560       SendMessage(hwnd, EM_UNDO, 0, 0);
5561       return 0;
5562     case IDM_SelectAll:
5563       sel.cpMin = 0;
5564       sel.cpMax = -1; /*999999?*/
5565       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
5566       return 0;
5567     case IDM_Cut:
5568       SendMessage(hwnd, WM_CUT, 0, 0);
5569       return 0;
5570     case IDM_Paste:
5571       SendMessage(hwnd, WM_PASTE, 0, 0);
5572       return 0;
5573     case IDM_Copy:
5574       SendMessage(hwnd, WM_COPY, 0, 0);
5575       return 0;
5576     }
5577     break;
5578   }
5579   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
5580 }
5581
5582 #define CO_MAX  100000
5583 #define CO_TRIM   1000
5584
5585 LRESULT CALLBACK
5586 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
5587 {
5588   static HWND hText, hInput, hFocus;
5589   InputSource *is = consoleInputSource;
5590   RECT rect;
5591   static int sizeX, sizeY;
5592   int newSizeX, newSizeY;
5593   MINMAXINFO *mmi;
5594
5595   switch (message) {
5596   case WM_INITDIALOG: /* message: initialize dialog box */
5597     hwndConsole = hDlg;
5598     hText = GetDlgItem(hDlg, OPT_ConsoleText);
5599     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
5600     SetFocus(hInput);
5601     consoleTextWindowProc = (WNDPROC)
5602       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
5603     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5604     consoleInputWindowProc = (WNDPROC)
5605       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
5606     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
5607     Colorize(ColorNormal, TRUE);
5608     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
5609     ChangedConsoleFont();
5610     GetClientRect(hDlg, &rect);
5611     sizeX = rect.right;
5612     sizeY = rect.bottom;
5613     if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&
5614         consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {
5615       WINDOWPLACEMENT wp;
5616       EnsureOnScreen(&consoleX, &consoleY);
5617       wp.length = sizeof(WINDOWPLACEMENT);
5618       wp.flags = 0;
5619       wp.showCmd = SW_SHOW;
5620       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
5621       wp.rcNormalPosition.left = consoleX;
5622       wp.rcNormalPosition.right = consoleX + consoleW;
5623       wp.rcNormalPosition.top = consoleY;
5624       wp.rcNormalPosition.bottom = consoleY + consoleH;
5625       SetWindowPlacement(hDlg, &wp);
5626     }
5627     return FALSE;
5628
5629   case WM_SETFOCUS:
5630     SetFocus(hInput);
5631     return 0;
5632
5633   case WM_CLOSE:
5634     ExitEvent(0);
5635     /* not reached */
5636     break;
5637
5638   case WM_SIZE:
5639     if (IsIconic(hDlg)) break;
5640     newSizeX = LOWORD(lParam);
5641     newSizeY = HIWORD(lParam);
5642     if (sizeX != newSizeX || sizeY != newSizeY) {
5643       RECT rectText, rectInput;
5644       POINT pt;
5645       int newTextHeight, newTextWidth;
5646       GetWindowRect(hText, &rectText);
5647       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
5648       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
5649       if (newTextHeight < 0) {
5650         newSizeY += -newTextHeight;
5651         newTextHeight = 0;
5652       }
5653       SetWindowPos(hText, NULL, 0, 0,
5654         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
5655       GetWindowRect(hInput, &rectInput); /* gives screen coords */
5656       pt.x = rectInput.left;
5657       pt.y = rectInput.top + newSizeY - sizeY;
5658       ScreenToClient(hDlg, &pt);
5659       SetWindowPos(hInput, NULL, 
5660         pt.x, pt.y, /* needs client coords */   
5661         rectInput.right - rectInput.left + newSizeX - sizeX,
5662         rectInput.bottom - rectInput.top, SWP_NOZORDER);
5663     }
5664     sizeX = newSizeX;
5665     sizeY = newSizeY;
5666     break;
5667
5668   case WM_GETMINMAXINFO:
5669     /* Prevent resizing window too small */
5670     mmi = (MINMAXINFO *) lParam;
5671     mmi->ptMinTrackSize.x = 100;
5672     mmi->ptMinTrackSize.y = 100;
5673     break;
5674   }
5675   return DefWindowProc(hDlg, message, wParam, lParam);
5676 }
5677
5678
5679 VOID
5680 ConsoleCreate()
5681 {
5682   HWND hCons;
5683   if (hwndConsole) return;
5684   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
5685   SendMessage(hCons, WM_INITDIALOG, 0, 0);
5686 }
5687
5688
5689 VOID
5690 ConsoleOutput(char* data, int length, int forceVisible)
5691 {
5692   HWND hText;
5693   int trim, exlen;
5694   char *p, *q;
5695   char buf[CO_MAX+1];
5696   POINT pEnd;
5697   RECT rect;
5698   static int delayLF = 0;
5699   CHARRANGE savesel, sel;
5700
5701   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
5702   p = data;
5703   q = buf;
5704   if (delayLF) {
5705     *q++ = '\r';
5706     *q++ = '\n';
5707     delayLF = 0;
5708   }
5709   while (length--) {
5710     if (*p == '\n') {
5711       if (*++p) {
5712         *q++ = '\r';
5713         *q++ = '\n';
5714       } else {
5715         delayLF = 1;
5716       }
5717     } else if (*p == '\007') {
5718        MyPlaySound(&sounds[(int)SoundBell]);
5719        p++;
5720     } else {
5721       *q++ = *p++;
5722     }
5723   }
5724   *q = NULLCHAR;
5725   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
5726   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5727   /* Save current selection */
5728   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
5729   exlen = GetWindowTextLength(hText);
5730   /* Find out whether current end of text is visible */
5731   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
5732   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
5733   /* Trim existing text if it's too long */
5734   if (exlen + (q - buf) > CO_MAX) {
5735     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
5736     sel.cpMin = 0;
5737     sel.cpMax = trim;
5738     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5739     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
5740     exlen -= trim;
5741     savesel.cpMin -= trim;
5742     savesel.cpMax -= trim;
5743     if (exlen < 0) exlen = 0;
5744     if (savesel.cpMin < 0) savesel.cpMin = 0;
5745     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
5746   }
5747   /* Append the new text */
5748   sel.cpMin = exlen;
5749   sel.cpMax = exlen;
5750   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5751   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
5752   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);
5753   if (forceVisible || exlen == 0 ||
5754       (rect.left <= pEnd.x && pEnd.x < rect.right &&
5755        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
5756     /* Scroll to make new end of text visible if old end of text
5757        was visible or new text is an echo of user typein */
5758     sel.cpMin = 9999999;
5759     sel.cpMax = 9999999;
5760     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5761     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5762     SendMessage(hText, EM_SCROLLCARET, 0, 0);
5763     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
5764   }
5765   if (savesel.cpMax == exlen || forceVisible) {
5766     /* Move insert point to new end of text if it was at the old
5767        end of text or if the new text is an echo of user typein */
5768     sel.cpMin = 9999999;
5769     sel.cpMax = 9999999;
5770     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
5771   } else {
5772     /* Restore previous selection */
5773     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
5774   }
5775   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
5776 }
5777
5778 /*---------*/
5779
5780
5781 void
5782 DisplayAClock(HDC hdc, int timeRemaining, int highlight,
5783               RECT *rect, char *color)
5784 {
5785   char buf[100];
5786   char *str;
5787   COLORREF oldFg, oldBg;
5788   HFONT oldFont;
5789
5790   if (appData.clockMode) {
5791     if (tinyLayout)
5792       sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));
5793     else
5794       sprintf(buf, "%s: %s", color, TimeString(timeRemaining));
5795     str = buf;
5796   } else {
5797     str = color;
5798   }
5799
5800   if (highlight) {
5801     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
5802     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
5803   } else {
5804     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
5805     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
5806   }
5807   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
5808
5809   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
5810              rect->top, ETO_CLIPPED|ETO_OPAQUE,
5811              rect, str, strlen(str), NULL);
5812
5813   (void) SetTextColor(hdc, oldFg);
5814   (void) SetBkColor(hdc, oldBg);
5815   (void) SelectObject(hdc, oldFont);
5816 }
5817
5818
5819 int
5820 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5821            OVERLAPPED *ovl)
5822 {
5823   int ok, err;
5824
5825   ResetEvent(ovl->hEvent);
5826   ovl->Offset = ovl->OffsetHigh = 0;
5827   ok = ReadFile(hFile, buf, count, outCount, ovl);
5828   if (ok) {
5829     err = NO_ERROR;
5830   } else {
5831     err = GetLastError();
5832     if (err == ERROR_IO_PENDING) {
5833       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5834       if (ok)
5835         err = NO_ERROR;
5836       else
5837         err = GetLastError();
5838     }
5839   }
5840   return err;
5841 }
5842
5843 int
5844 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
5845             OVERLAPPED *ovl)
5846 {
5847   int ok, err;
5848
5849   ResetEvent(ovl->hEvent);
5850   ovl->Offset = ovl->OffsetHigh = 0;
5851   ok = WriteFile(hFile, buf, count, outCount, ovl);
5852   if (ok) {
5853     err = NO_ERROR;
5854   } else {
5855     err = GetLastError();
5856     if (err == ERROR_IO_PENDING) {
5857       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
5858       if (ok)
5859         err = NO_ERROR;
5860       else
5861         err = GetLastError();
5862     }
5863   }
5864   return err;
5865 }
5866
5867
5868 DWORD
5869 InputThread(LPVOID arg)
5870 {
5871   InputSource *is;
5872   OVERLAPPED ovl;
5873
5874   is = (InputSource *) arg;
5875   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
5876   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
5877   while (is->hThread != NULL) {
5878     is->error = DoReadFile(is->hFile, is->next,
5879                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5880                            &is->count, &ovl);
5881     if (is->error == NO_ERROR) {
5882       is->next += is->count;
5883     } else {
5884       if (is->error == ERROR_BROKEN_PIPE) {
5885         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */
5886         is->count = 0;
5887       } else {
5888         is->count = (DWORD) -1;
5889       }
5890     }
5891     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5892     if (is->count <= 0) break;  /* Quit on EOF or error */
5893   }
5894   CloseHandle(ovl.hEvent);
5895   CloseHandle(is->hFile);
5896   return 0;
5897 }
5898
5899
5900 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
5901 DWORD
5902 NonOvlInputThread(LPVOID arg)
5903 {
5904   InputSource *is;
5905   char *p, *q;
5906   int i;
5907   char prev;
5908
5909   is = (InputSource *) arg;
5910   while (is->hThread != NULL) {
5911     is->error = ReadFile(is->hFile, is->next,
5912                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
5913                          &is->count, NULL) ? NO_ERROR : GetLastError();
5914     if (is->error == NO_ERROR) {
5915       /* Change CRLF to LF */
5916       if (is->next > is->buf) {
5917         p = is->next - 1;
5918         i = is->count + 1;
5919       } else {
5920         p = is->next;
5921         i = is->count;
5922       }
5923       q = p;
5924       prev = NULLCHAR;
5925       while (i > 0) {
5926         if (prev == '\r' && *p == '\n') {
5927           *(q-1) = '\n';
5928           is->count--;
5929         } else { 
5930           *q++ = *p;
5931         }
5932         prev = *p++;
5933         i--;
5934       }
5935       *q = NULLCHAR;
5936       is->next = q;
5937     } else {
5938       if (is->error == ERROR_BROKEN_PIPE) {
5939         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */
5940         is->count = 0; 
5941       } else {
5942         is->count = (DWORD) -1;
5943       }
5944     }
5945     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5946     if (is->count < 0) break;  /* Quit on error */
5947   }
5948   CloseHandle(is->hFile);
5949   return 0;
5950 }
5951
5952 DWORD
5953 SocketInputThread(LPVOID arg)
5954 {
5955   InputSource *is;
5956
5957   is = (InputSource *) arg;
5958   while (is->hThread != NULL) {
5959     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
5960     if ((int)is->count == SOCKET_ERROR) {
5961       is->count = (DWORD) -1;
5962       is->error = WSAGetLastError();
5963     } else {
5964       is->error = NO_ERROR;
5965       is->next += is->count;
5966       if (is->count == 0 && is->second == is) {
5967         /* End of file on stderr; quit with no message */
5968         break;
5969       }
5970     }
5971     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
5972     if (is->count <= 0) break;  /* Quit on EOF or error */
5973   }
5974   return 0;
5975 }
5976
5977 VOID
5978 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
5979 {
5980   InputSource *is;
5981
5982   is = (InputSource *) lParam;
5983   if (is->lineByLine) {
5984     /* Feed in lines one by one */
5985     char *p = is->buf;
5986     char *q = p;
5987     while (q < is->next) {
5988       if (*q++ == '\n') {
5989         (is->func)(is, is->closure, p, q - p, NO_ERROR);
5990         p = q;
5991       }
5992     }
5993     /* Move any partial line to the start of the buffer */
5994     q = is->buf;
5995     while (p < is->next) {
5996       *q++ = *p++;
5997     }
5998     is->next = q;
5999     if (is->error != NO_ERROR || is->count == 0) {
6000       /* Notify backend of the error.  Note: If there was a partial
6001          line at the end, it is not flushed through. */
6002       (is->func)(is, is->closure, is->buf, is->count, is->error);   
6003     }
6004   } else {
6005     /* Feed in the whole chunk of input at once */
6006     (is->func)(is, is->closure, is->buf, is->count, is->error);
6007     is->next = is->buf;
6008   }
6009 }
6010
6011 /*---------------------------------------------------------------------------*\
6012  *
6013  *  Menu enables. Used when setting various modes.
6014  *
6015 \*---------------------------------------------------------------------------*/
6016
6017 typedef struct {
6018   int item;
6019   int flags;
6020 } Enables;
6021
6022 VOID
6023 SetMenuEnables(HMENU hmenu, Enables *enab)
6024 {
6025   while (enab->item > 0) {
6026     (void) EnableMenuItem(hmenu, enab->item, enab->flags);
6027     enab++;
6028   }
6029 }
6030
6031 Enables gnuEnables[] = {
6032   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6033   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6034   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6035   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
6036   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
6037   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
6038   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6039   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
6040   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
6041   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6042   { -1, -1 }
6043 };
6044
6045 Enables icsEnables[] = {
6046   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6047   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6048   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6049   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6050   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6051   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6052   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6053   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6054   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6055   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6056   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6057   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
6058   { -1, -1 }
6059 };
6060
6061 #ifdef ZIPPY
6062 Enables zippyEnables[] = {
6063   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6064   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
6065   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
6066   { -1, -1 }
6067 };
6068 #endif
6069
6070 Enables ncpEnables[] = {
6071   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
6072   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
6073   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6074   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6075   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6076   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
6077   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
6078   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
6079   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
6080   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
6081   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6082   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6083   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
6084   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
6085   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
6086   { -1, -1 }
6087 };
6088
6089 Enables trainingOnEnables[] = {
6090   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
6091   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
6092   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
6093   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
6094   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
6095   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
6096   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
6097   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
6098   { -1, -1 }
6099 };
6100
6101 Enables trainingOffEnables[] = {
6102   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
6103   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
6104   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
6105   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
6106   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
6107   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
6108   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
6109   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
6110   { -1, -1 }
6111 };
6112
6113 /* These modify either ncpEnables or gnuEnables */
6114 Enables cmailEnables[] = {
6115   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
6116   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
6117   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
6118   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
6119   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
6120   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
6121   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
6122   { -1, -1 }
6123 };
6124
6125 Enables machineThinkingEnables[] = {
6126   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
6127   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
6128   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
6129   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
6130   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
6131   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
6132   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
6133   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
6134   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
6135   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
6136   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
6137   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
6138   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
6139   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
6140   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
6141   { -1, -1 }
6142 };
6143
6144 Enables userThinkingEnables[] = {
6145   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
6146   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
6147   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
6148   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
6149   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
6150   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
6151   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
6152   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
6153   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
6154   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
6155   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
6156   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
6157   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
6158   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
6159   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
6160   { -1, -1 }
6161 };
6162
6163 /*---------------------------------------------------------------------------*\
6164  *
6165  *  Front-end interface functions exported by XBoard.
6166  *  Functions appear in same order as prototypes in frontend.h.
6167  * 
6168 \*---------------------------------------------------------------------------*/
6169 VOID
6170 ModeHighlight()
6171 {
6172   static UINT prevChecked = 0;
6173   static int prevPausing = 0;
6174   UINT nowChecked;
6175
6176   if (pausing != prevPausing) {
6177     prevPausing = pausing;
6178     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
6179                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
6180     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
6181   }
6182
6183   switch (gameMode) {
6184   case BeginningOfGame:
6185     if (appData.icsActive)
6186       nowChecked = IDM_IcsClient;
6187     else if (appData.noChessProgram)
6188       nowChecked = IDM_EditGame;
6189     else
6190       nowChecked = IDM_MachineBlack;
6191     break;
6192   case MachinePlaysBlack:
6193     nowChecked = IDM_MachineBlack;
6194     break;
6195   case MachinePlaysWhite:
6196     nowChecked = IDM_MachineWhite;
6197     break;
6198   case TwoMachinesPlay:
6199     nowChecked = IDM_TwoMachines;
6200     break;
6201   case AnalyzeMode:
6202     nowChecked = IDM_AnalysisMode;
6203     break;
6204   case AnalyzeFile:
6205     nowChecked = IDM_AnalyzeFile;
6206     break;
6207   case EditGame:
6208     nowChecked = IDM_EditGame;
6209     break;
6210   case PlayFromGameFile:
6211     nowChecked = IDM_LoadGame;
6212     break;
6213   case EditPosition:
6214     nowChecked = IDM_EditPosition;
6215     break;
6216   case Training:
6217     nowChecked = IDM_Training;
6218     break;
6219   case IcsPlayingWhite:
6220   case IcsPlayingBlack:
6221   case IcsObserving:
6222   case IcsIdle:
6223     nowChecked = IDM_IcsClient;
6224     break;
6225   default:
6226   case EndOfGame:
6227     nowChecked = 0;
6228     break;
6229   }
6230   if (prevChecked != 0)
6231     (void) CheckMenuItem(GetMenu(hwndMain),
6232                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
6233   if (nowChecked != 0)
6234     (void) CheckMenuItem(GetMenu(hwndMain),
6235                          nowChecked, MF_BYCOMMAND|MF_CHECKED);
6236
6237   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
6238     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, 
6239                           MF_BYCOMMAND|MF_ENABLED);
6240   } else {
6241     (void) EnableMenuItem(GetMenu(hwndMain), 
6242                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);
6243   }
6244
6245   prevChecked = nowChecked;
6246   /* icsEngineAnalyze - Do a sceure check too */
6247   if (appData.icsEngineAnalyze) {
6248         (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6249                 MF_BYCOMMAND|MF_CHECKED);
6250   } else {
6251         (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6252                 MF_BYCOMMAND|MF_UNCHECKED);
6253   }
6254 }
6255
6256 VOID
6257 SetICSMode()
6258 {
6259   HMENU hmenu = GetMenu(hwndMain);
6260   SetMenuEnables(hmenu, icsEnables);
6261   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
6262     MF_BYPOSITION|MF_ENABLED);
6263 #ifdef ZIPPY
6264   if (appData.zippyPlay) {
6265     SetMenuEnables(hmenu, zippyEnables);
6266         /* icsEngineAnalyze */
6267     if (!appData.noChessProgram) 
6268           (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,
6269            MF_BYCOMMAND|MF_ENABLED);
6270   }
6271 #endif
6272 }
6273
6274 VOID
6275 SetGNUMode()
6276 {
6277   SetMenuEnables(GetMenu(hwndMain), gnuEnables);
6278 }
6279
6280 VOID
6281 SetNCPMode()
6282 {
6283   HMENU hmenu = GetMenu(hwndMain);
6284   SetMenuEnables(hmenu, ncpEnables);
6285   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
6286     MF_BYPOSITION|MF_GRAYED);
6287     DrawMenuBar(hwndMain);
6288 }
6289
6290 VOID
6291 SetCmailMode()
6292 {
6293   SetMenuEnables(GetMenu(hwndMain), cmailEnables);
6294 }
6295
6296 VOID 
6297 SetTrainingModeOn()
6298 {
6299   int i;
6300   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
6301   for (i = 0; i < N_BUTTONS; i++) {
6302     if (buttonDesc[i].hwnd != NULL)
6303       EnableWindow(buttonDesc[i].hwnd, FALSE);
6304   }
6305   CommentPopDown();
6306 }
6307
6308 VOID SetTrainingModeOff()
6309 {
6310   int i;
6311   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
6312   for (i = 0; i < N_BUTTONS; i++) {
6313     if (buttonDesc[i].hwnd != NULL)
6314       EnableWindow(buttonDesc[i].hwnd, TRUE);
6315   }
6316 }
6317
6318
6319 VOID
6320 SetUserThinkingEnables()
6321 {
6322   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
6323 }
6324
6325 VOID
6326 SetMachineThinkingEnables()
6327 {
6328   HMENU hMenu = GetMenu(hwndMain);
6329   int flags = MF_BYCOMMAND|MF_ENABLED;
6330
6331   SetMenuEnables(hMenu, machineThinkingEnables);
6332
6333   if (gameMode == MachinePlaysBlack) {
6334     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
6335   } else if (gameMode == MachinePlaysWhite) {
6336     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
6337   } else if (gameMode == TwoMachinesPlay) {
6338     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
6339   }
6340 }
6341
6342
6343 VOID
6344 DisplayTitle(char *str)
6345 {
6346   char title[MSG_SIZ], *host;
6347   if (str[0] != NULLCHAR) {
6348     strcpy(title, str);
6349   } else if (appData.icsActive) {
6350     if (appData.icsCommPort[0] != NULLCHAR)
6351       host = "ICS";
6352     else 
6353       host = appData.icsHost;
6354     sprintf(title, "%s: %s", szTitle, host);
6355   } else if (appData.noChessProgram) {
6356     strcpy(title, szTitle);
6357   } else {
6358     strcpy(title, szTitle);
6359     strcat(title, ": ");
6360     strcat(title, first.tidy);
6361   }
6362   SetWindowText(hwndMain, title);
6363 }
6364
6365
6366 VOID
6367 DisplayMessage(char *str1, char *str2)
6368 {
6369   HDC hdc;
6370   HFONT oldFont;
6371   int remain = MESSAGE_TEXT_MAX - 1;
6372   int len;
6373
6374   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
6375   messageText[0] = NULLCHAR;
6376   if (*str1) {
6377     len = strlen(str1);
6378     if (len > remain) len = remain;
6379     strncpy(messageText, str1, len);
6380     messageText[len] = NULLCHAR;
6381     remain -= len;
6382   }
6383   if (*str2 && remain >= 2) {
6384     if (*str1) {
6385       strcat(messageText, "  ");
6386       remain -= 2;
6387     }
6388     len = strlen(str2);
6389     if (len > remain) len = remain;
6390     strncat(messageText, str2, len);
6391   }
6392   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
6393
6394   if (IsIconic(hwndMain)) return;
6395   hdc = GetDC(hwndMain);
6396   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
6397   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
6398              &messageRect, messageText, strlen(messageText), NULL);
6399   (void) SelectObject(hdc, oldFont);
6400   (void) ReleaseDC(hwndMain, hdc);
6401 }
6402
6403 VOID
6404 DisplayError(char *str, int error)
6405 {
6406   char buf[MSG_SIZ*2], buf2[MSG_SIZ];
6407   int len;
6408  
6409   if (error == 0) {
6410     strcpy(buf, str);
6411   } else {
6412     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6413                         NULL, error, LANG_NEUTRAL,
6414                         (LPSTR) buf2, MSG_SIZ, NULL);
6415     if (len > 0) {
6416       sprintf(buf, "%s:\n%s", str, buf2);
6417     } else {
6418       ErrorMap *em = errmap;
6419       while (em->err != 0 && em->err != error) em++;
6420       if (em->err != 0) {
6421         sprintf(buf, "%s:\n%s", str, em->msg);
6422       } else {
6423         sprintf(buf, "%s:\nError code %d", str, error);
6424       }
6425     }
6426   }
6427   
6428   ErrorPopUp("Error", buf);
6429 }
6430
6431
6432 VOID
6433 DisplayMoveError(char *str)
6434 {
6435   fromX = fromY = -1;
6436   ClearHighlights();
6437   DrawPosition(FALSE, NULL);
6438   if (appData.popupMoveErrors) {
6439     ErrorPopUp("Error", str);
6440   } else {
6441     DisplayMessage(str, "");
6442     moveErrorMessageUp = TRUE;
6443   }
6444 }
6445
6446 VOID
6447 DisplayFatalError(char *str, int error, int exitStatus)
6448 {
6449   char buf[2*MSG_SIZ], buf2[MSG_SIZ];
6450   int len;
6451   char *label = exitStatus ? "Fatal Error" : "Exiting";
6452
6453   if (error != 0) {
6454     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
6455                         NULL, error, LANG_NEUTRAL,
6456                         (LPSTR) buf2, MSG_SIZ, NULL);
6457     if (len > 0) {
6458       sprintf(buf, "%s:\n%s", str, buf2);
6459     } else {
6460       ErrorMap *em = errmap;
6461       while (em->err != 0 && em->err != error) em++;
6462       if (em->err != 0) {
6463         sprintf(buf, "%s:\n%s", str, em->msg);
6464       } else {
6465         sprintf(buf, "%s:\nError code %d", str, error);
6466       }
6467     }
6468     str = buf;
6469   }
6470   if (appData.debugMode) {
6471     fprintf(debugFP, "%s: %s\n", label, str);
6472   }
6473   if (appData.popupExitMessage) {
6474     (void) MessageBox(hwndMain, str, label, MB_OK|
6475                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
6476   }
6477   ExitEvent(exitStatus);
6478 }
6479
6480
6481 VOID
6482 DisplayInformation(char *str)
6483 {
6484   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
6485 }
6486
6487
6488 VOID
6489 DisplayNote(char *str)
6490 {
6491   ErrorPopUp("Note", str);
6492 }
6493
6494
6495 typedef struct {
6496   char *title, *question, *replyPrefix;
6497   ProcRef pr;
6498 } QuestionParams;
6499
6500 LRESULT CALLBACK
6501 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
6502 {
6503   static QuestionParams *qp;
6504   char reply[MSG_SIZ];
6505   int len, err;
6506
6507   switch (message) {
6508   case WM_INITDIALOG:
6509     qp = (QuestionParams *) lParam;
6510     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
6511     SetWindowText(hDlg, qp->title);
6512     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
6513     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
6514     return FALSE;
6515
6516   case WM_COMMAND:
6517     switch (LOWORD(wParam)) {
6518     case IDOK:
6519       strcpy(reply, qp->replyPrefix);
6520       if (*reply) strcat(reply, " ");
6521       len = strlen(reply);
6522       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
6523       strcat(reply, "\n");
6524       OutputToProcess(qp->pr, reply, strlen(reply), &err);
6525       EndDialog(hDlg, TRUE);
6526       if (err) DisplayFatalError("Error writing to chess program", err, 1);
6527       return TRUE;
6528     case IDCANCEL:
6529       EndDialog(hDlg, FALSE);
6530       return TRUE;
6531     default:
6532       break;
6533     }
6534     break;
6535   }
6536   return FALSE;
6537 }
6538
6539 VOID
6540 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
6541 {
6542     QuestionParams qp;
6543     FARPROC lpProc;
6544     
6545     qp.title = title;
6546     qp.question = question;
6547     qp.replyPrefix = replyPrefix;
6548     qp.pr = pr;
6549     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
6550     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
6551       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
6552     FreeProcInstance(lpProc);
6553 }
6554
6555
6556 VOID
6557 DisplayIcsInteractionTitle(char *str)
6558 {
6559   char consoleTitle[MSG_SIZ];
6560
6561   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
6562   SetWindowText(hwndConsole, consoleTitle);
6563 }
6564
6565 void
6566 DrawPosition(int fullRedraw, Board board)
6567 {
6568   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); 
6569 }
6570
6571
6572 VOID
6573 ResetFrontEnd()
6574 {
6575   fromX = fromY = -1;
6576   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
6577     dragInfo.pos.x = dragInfo.pos.y = -1;
6578     dragInfo.pos.x = dragInfo.pos.y = -1;
6579     dragInfo.lastpos = dragInfo.pos;
6580     dragInfo.start.x = dragInfo.start.y = -1;
6581     dragInfo.from = dragInfo.start;
6582     ReleaseCapture();
6583     DrawPosition(TRUE, NULL);
6584   }
6585 }
6586
6587
6588 VOID
6589 CommentPopUp(char *title, char *str)
6590 {
6591   HWND hwnd = GetActiveWindow();
6592   EitherCommentPopUp(0, title, str, FALSE);
6593   SetActiveWindow(hwnd);
6594 }
6595
6596 VOID
6597 CommentPopDown(void)
6598 {
6599   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
6600   if (commentDialog) {
6601     ShowWindow(commentDialog, SW_HIDE);
6602   }
6603   commentDialogUp = FALSE;
6604 }
6605
6606 VOID
6607 EditCommentPopUp(int index, char *title, char *str)
6608 {
6609   EitherCommentPopUp(index, title, str, TRUE);
6610 }
6611
6612
6613 VOID
6614 RingBell()
6615 {
6616   MyPlaySound(&sounds[(int)SoundMove]);
6617 }
6618
6619 VOID PlayIcsWinSound()
6620 {
6621   MyPlaySound(&sounds[(int)SoundIcsWin]);
6622 }
6623
6624 VOID PlayIcsLossSound()
6625 {
6626   MyPlaySound(&sounds[(int)SoundIcsLoss]);
6627 }
6628
6629 VOID PlayIcsDrawSound()
6630 {
6631   MyPlaySound(&sounds[(int)SoundIcsDraw]);
6632 }
6633
6634 VOID PlayIcsUnfinishedSound()
6635 {
6636   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
6637 }
6638
6639 VOID
6640 PlayAlarmSound()
6641 {
6642   MyPlaySound(&sounds[(int)SoundAlarm]);
6643 }
6644
6645
6646 VOID
6647 EchoOn()
6648 {
6649   HWND hInput;
6650   consoleEcho = TRUE;
6651   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6652   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
6653   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
6654 }
6655
6656
6657 VOID
6658 EchoOff()
6659 {
6660   CHARFORMAT cf;
6661   HWND hInput;
6662   consoleEcho = FALSE;
6663   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
6664   /* This works OK: set text and background both to the same color */
6665   cf = consoleCF;
6666   cf.crTextColor = COLOR_ECHOOFF;
6667   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
6668   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
6669 }
6670
6671 /* No Raw()...? */
6672
6673 void Colorize(ColorClass cc, int continuation)
6674 {
6675   currentColorClass = cc;
6676   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
6677   consoleCF.crTextColor = textAttribs[cc].color;
6678   consoleCF.dwEffects = textAttribs[cc].effects;
6679   if (!continuation) MyPlaySound(&textAttribs[cc].sound);
6680 }
6681
6682 char *
6683 UserName()
6684 {
6685   static char buf[MSG_SIZ];
6686   DWORD bufsiz = MSG_SIZ;
6687
6688   if (!GetUserName(buf, &bufsiz)) {
6689     /*DisplayError("Error getting user name", GetLastError());*/
6690     strcpy(buf, "User");
6691   }
6692   return buf;
6693 }
6694
6695 char *
6696 HostName()
6697 {
6698   static char buf[MSG_SIZ];
6699   DWORD bufsiz = MSG_SIZ;
6700
6701   if (!GetComputerName(buf, &bufsiz)) {
6702     /*DisplayError("Error getting host name", GetLastError());*/
6703     strcpy(buf, "Unknown");
6704   }
6705   return buf;
6706 }
6707
6708
6709 int
6710 ClockTimerRunning()
6711 {
6712   return clockTimerEvent != 0;
6713 }
6714
6715 int
6716 StopClockTimer()
6717 {
6718   if (clockTimerEvent == 0) return FALSE;
6719   KillTimer(hwndMain, clockTimerEvent);
6720   clockTimerEvent = 0;
6721   return TRUE;
6722 }
6723
6724 void
6725 StartClockTimer(long millisec)
6726 {
6727   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
6728                              (UINT) millisec, NULL);
6729 }
6730
6731 void
6732 DisplayWhiteClock(long timeRemaining, int highlight)
6733 {
6734   HDC hdc;
6735   hdc = GetDC(hwndMain);
6736   if (!IsIconic(hwndMain)) {
6737     DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");
6738   }
6739   if (highlight && iconCurrent == iconBlack) {
6740     iconCurrent = iconWhite;
6741     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6742     if (IsIconic(hwndMain)) {
6743       DrawIcon(hdc, 2, 2, iconCurrent);
6744     }
6745   }
6746   (void) ReleaseDC(hwndMain, hdc);
6747   if (hwndConsole)
6748     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6749 }
6750
6751 void
6752 DisplayBlackClock(long timeRemaining, int highlight)
6753 {
6754   HDC hdc;
6755   hdc = GetDC(hwndMain);
6756   if (!IsIconic(hwndMain)) {
6757     DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");
6758   }
6759   if (highlight && iconCurrent == iconWhite) {
6760     iconCurrent = iconBlack;
6761     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6762     if (IsIconic(hwndMain)) {
6763       DrawIcon(hdc, 2, 2, iconCurrent);
6764     }
6765   }
6766   (void) ReleaseDC(hwndMain, hdc);
6767   if (hwndConsole)
6768     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
6769 }
6770
6771
6772 int
6773 LoadGameTimerRunning()
6774 {
6775   return loadGameTimerEvent != 0;
6776 }
6777
6778 int
6779 StopLoadGameTimer()
6780 {
6781   if (loadGameTimerEvent == 0) return FALSE;
6782   KillTimer(hwndMain, loadGameTimerEvent);
6783   loadGameTimerEvent = 0;
6784   return TRUE;
6785 }
6786
6787 void
6788 StartLoadGameTimer(long millisec)
6789 {
6790   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
6791                                 (UINT) millisec, NULL);
6792 }
6793
6794 void
6795 AutoSaveGame()
6796 {
6797   char *defName;
6798   FILE *f;
6799   char fileTitle[MSG_SIZ];
6800
6801   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
6802   f = OpenFileDialog(hwndMain, TRUE, defName,
6803                      appData.oldSaveStyle ? "gam" : "pgn",
6804                      GAME_FILT, 
6805                      "Save Game to File", NULL, fileTitle, NULL);
6806   if (f != NULL) {
6807     SaveGame(f, 0, "");
6808     fclose(f);
6809   }
6810 }
6811
6812
6813 void
6814 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
6815 {
6816   if (delayedTimerEvent != 0) {
6817     if (appData.debugMode) {
6818       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
6819     }
6820     KillTimer(hwndMain, delayedTimerEvent);
6821     delayedTimerEvent = 0;
6822     delayedTimerCallback();
6823   }
6824   delayedTimerCallback = cb;
6825   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
6826                                 (UINT) millisec, NULL);
6827 }
6828
6829 DelayedEventCallback
6830 GetDelayedEvent()
6831 {
6832   if (delayedTimerEvent) {
6833     return delayedTimerCallback;
6834   } else {
6835     return NULL;
6836   }
6837 }
6838
6839 void
6840 CancelDelayedEvent()
6841 {
6842   if (delayedTimerEvent) {
6843     KillTimer(hwndMain, delayedTimerEvent);
6844     delayedTimerEvent = 0;
6845   }
6846 }
6847
6848 /* Start a child process running the given program.
6849    The process's standard output can be read from "from", and its
6850    standard input can be written to "to".
6851    Exit with fatal error if anything goes wrong.
6852    Returns an opaque pointer that can be used to destroy the process
6853    later.
6854 */
6855 int
6856 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
6857 {
6858 #define BUFSIZE 4096
6859
6860   HANDLE hChildStdinRd, hChildStdinWr,
6861     hChildStdoutRd, hChildStdoutWr;
6862   HANDLE hChildStdinWrDup, hChildStdoutRdDup;
6863   SECURITY_ATTRIBUTES saAttr;
6864   BOOL fSuccess;
6865   PROCESS_INFORMATION piProcInfo;
6866   STARTUPINFO siStartInfo;
6867   ChildProc *cp;
6868   char buf[MSG_SIZ];
6869   DWORD err;
6870
6871   if (appData.debugMode) {
6872     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
6873   }
6874
6875   *pr = NoProc;
6876
6877   /* Set the bInheritHandle flag so pipe handles are inherited. */
6878   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
6879   saAttr.bInheritHandle = TRUE;
6880   saAttr.lpSecurityDescriptor = NULL;
6881
6882   /*
6883    * The steps for redirecting child's STDOUT:
6884    *     1. Create anonymous pipe to be STDOUT for child.
6885    *     2. Create a noninheritable duplicate of read handle,
6886    *         and close the inheritable read handle.
6887    */
6888
6889   /* Create a pipe for the child's STDOUT. */
6890   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
6891     return GetLastError();
6892   }
6893
6894   /* Duplicate the read handle to the pipe, so it is not inherited. */
6895   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
6896                              GetCurrentProcess(), &hChildStdoutRdDup, 0,
6897                              FALSE,     /* not inherited */
6898                              DUPLICATE_SAME_ACCESS);
6899   if (! fSuccess) {
6900     return GetLastError();
6901   }
6902   CloseHandle(hChildStdoutRd);
6903
6904   /*
6905    * The steps for redirecting child's STDIN:
6906    *     1. Create anonymous pipe to be STDIN for child.
6907    *     2. Create a noninheritable duplicate of write handle,
6908    *         and close the inheritable write handle.
6909    */
6910
6911   /* Create a pipe for the child's STDIN. */
6912   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
6913     return GetLastError();
6914   }
6915
6916   /* Duplicate the write handle to the pipe, so it is not inherited. */
6917   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
6918                              GetCurrentProcess(), &hChildStdinWrDup, 0,
6919                              FALSE,     /* not inherited */
6920                              DUPLICATE_SAME_ACCESS);
6921   if (! fSuccess) {
6922     return GetLastError();
6923   }
6924   CloseHandle(hChildStdinWr);
6925
6926   /* Arrange to (1) look in dir for the child .exe file, and
6927    * (2) have dir be the child's working directory.  Interpret
6928    * dir relative to the directory WinBoard loaded from. */
6929   GetCurrentDirectory(MSG_SIZ, buf);
6930   SetCurrentDirectory(installDir);
6931   SetCurrentDirectory(dir);
6932
6933   /* Now create the child process. */
6934
6935   siStartInfo.cb = sizeof(STARTUPINFO);
6936   siStartInfo.lpReserved = NULL;
6937   siStartInfo.lpDesktop = NULL;
6938   siStartInfo.lpTitle = NULL;
6939   siStartInfo.dwFlags = STARTF_USESTDHANDLES;
6940   siStartInfo.cbReserved2 = 0;
6941   siStartInfo.lpReserved2 = NULL;
6942   siStartInfo.hStdInput = hChildStdinRd;
6943   siStartInfo.hStdOutput = hChildStdoutWr;
6944   siStartInfo.hStdError = hChildStdoutWr;
6945
6946   fSuccess = CreateProcess(NULL,
6947                            cmdLine,        /* command line */
6948                            NULL,           /* process security attributes */
6949                            NULL,           /* primary thread security attrs */
6950                            TRUE,           /* handles are inherited */
6951                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
6952                            NULL,           /* use parent's environment */
6953                            NULL,
6954                            &siStartInfo, /* STARTUPINFO pointer */
6955                            &piProcInfo); /* receives PROCESS_INFORMATION */
6956
6957   err = GetLastError();
6958   SetCurrentDirectory(buf); /* return to prev directory */
6959   if (! fSuccess) {
6960     return err;
6961   }
6962
6963   /* Close the handles we don't need in the parent */
6964   CloseHandle(piProcInfo.hThread);
6965   CloseHandle(hChildStdinRd);
6966   CloseHandle(hChildStdoutWr);
6967
6968   /* Prepare return value */
6969   cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6970   cp->kind = CPReal;
6971   cp->hProcess = piProcInfo.hProcess;
6972   cp->pid = piProcInfo.dwProcessId;
6973   cp->hFrom = hChildStdoutRdDup;
6974   cp->hTo = hChildStdinWrDup;
6975
6976   *pr = (void *) cp;
6977
6978   /* Klaus Friedel says that this Sleep solves a problem under Windows
6979      2000 where engines sometimes don't see the initial command(s)
6980      from WinBoard and hang.  I don't understand how that can happen,
6981      but the Sleep is harmless, so I've put it in.  Others have also
6982      reported what may be the same problem, so hopefully this will fix
6983      it for them too.  */
6984   Sleep(500);
6985
6986   return NO_ERROR;
6987 }
6988
6989
6990 void
6991 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
6992 {
6993   ChildProc *cp;
6994
6995   cp = (ChildProc *) pr;
6996   if (cp == NULL) return;
6997
6998   switch (cp->kind) {
6999   case CPReal:
7000     /* TerminateProcess is considered harmful, so... */
7001     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
7002     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */
7003     /* The following doesn't work because the chess program
7004        doesn't "have the same console" as WinBoard.  Maybe
7005        we could arrange for this even though neither WinBoard
7006        nor the chess program uses a console for stdio? */
7007     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
7008     CloseHandle(cp->hProcess);
7009     break;
7010
7011   case CPComm:
7012     if (cp->hFrom) CloseHandle(cp->hFrom);
7013     break;
7014
7015   case CPSock:
7016     closesocket(cp->sock);
7017     WSACleanup();
7018     break;
7019
7020   case CPRcmd:
7021     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */
7022     closesocket(cp->sock);
7023     closesocket(cp->sock2);
7024     WSACleanup();
7025     break;
7026   }
7027   free(cp);
7028 }
7029
7030 void
7031 InterruptChildProcess(ProcRef pr)
7032 {
7033   ChildProc *cp;
7034
7035   cp = (ChildProc *) pr;
7036   if (cp == NULL) return;
7037   switch (cp->kind) {
7038   case CPReal:
7039     /* The following doesn't work because the chess program
7040        doesn't "have the same console" as WinBoard.  Maybe
7041        we could arrange for this even though neither WinBoard
7042        nor the chess program uses a console for stdio */
7043     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
7044     break;
7045
7046   case CPComm:
7047   case CPSock:
7048     /* Can't interrupt */
7049     break;
7050
7051   case CPRcmd:
7052     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */
7053     break;
7054   }
7055 }
7056
7057
7058 int
7059 OpenTelnet(char *host, char *port, ProcRef *pr)
7060 {
7061   char cmdLine[MSG_SIZ];
7062
7063   if (port[0] == NULLCHAR) {
7064     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
7065   } else {
7066     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
7067   }
7068   return StartChildProcess(cmdLine, "", pr);
7069 }
7070
7071
7072 /* Code to open TCP sockets */
7073
7074 int
7075 OpenTCP(char *host, char *port, ProcRef *pr)
7076 {
7077   ChildProc *cp;
7078   int err;
7079   SOCKET s;
7080   struct sockaddr_in sa, mysa;
7081   struct hostent FAR *hp;
7082   unsigned short uport;
7083   WORD wVersionRequested;
7084   WSADATA wsaData;
7085
7086   /* Initialize socket DLL */
7087   wVersionRequested = MAKEWORD(1, 1);
7088   err = WSAStartup(wVersionRequested, &wsaData);
7089   if (err != 0) return err;
7090
7091   /* Make socket */
7092   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7093     err = WSAGetLastError();
7094     WSACleanup();
7095     return err;
7096   }
7097
7098   /* Bind local address using (mostly) don't-care values.
7099    */
7100   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7101   mysa.sin_family = AF_INET;
7102   mysa.sin_addr.s_addr = INADDR_ANY;
7103   uport = (unsigned short) 0;
7104   mysa.sin_port = htons(uport);
7105   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7106       == SOCKET_ERROR) {
7107     err = WSAGetLastError();
7108     WSACleanup();
7109     return err;
7110   }
7111
7112   /* Resolve remote host name */
7113   memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7114   if (!(hp = gethostbyname(host))) {
7115     unsigned int b0, b1, b2, b3;
7116
7117     err = WSAGetLastError();
7118
7119     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7120       hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7121       hp->h_addrtype = AF_INET;
7122       hp->h_length = 4;
7123       hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7124       hp->h_addr_list[0] = (char *) malloc(4);
7125       hp->h_addr_list[0][0] = (char) b0;
7126       hp->h_addr_list[0][1] = (char) b1;
7127       hp->h_addr_list[0][2] = (char) b2;
7128       hp->h_addr_list[0][3] = (char) b3;
7129     } else {
7130       WSACleanup();
7131       return err;
7132     }
7133   }
7134   sa.sin_family = hp->h_addrtype;
7135   uport = (unsigned short) atoi(port);
7136   sa.sin_port = htons(uport);
7137   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7138
7139   /* Make connection */
7140   if (connect(s, (struct sockaddr *) &sa,
7141               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7142     err = WSAGetLastError();
7143     WSACleanup();
7144     return err;
7145   }
7146
7147   /* Prepare return value */
7148   cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7149   cp->kind = CPSock;
7150   cp->sock = s;
7151   *pr = (ProcRef *) cp;
7152
7153   return NO_ERROR;
7154 }
7155
7156 int
7157 OpenCommPort(char *name, ProcRef *pr)
7158 {
7159   HANDLE h;
7160   COMMTIMEOUTS ct;
7161   ChildProc *cp;
7162   char fullname[MSG_SIZ];
7163
7164   if (*name != '\\')
7165     sprintf(fullname, "\\\\.\\%s", name);
7166   else
7167     strcpy(fullname, name);
7168
7169   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
7170                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
7171   if (h == (HANDLE) -1) {
7172     return GetLastError();
7173   }
7174   hCommPort = h;
7175
7176   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
7177
7178   /* Accumulate characters until a 100ms pause, then parse */
7179   ct.ReadIntervalTimeout = 100;
7180   ct.ReadTotalTimeoutMultiplier = 0;
7181   ct.ReadTotalTimeoutConstant = 0;
7182   ct.WriteTotalTimeoutMultiplier = 0;
7183   ct.WriteTotalTimeoutConstant = 0;
7184   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
7185
7186   /* Prepare return value */
7187   cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7188   cp->kind = CPComm;
7189   cp->hFrom = h;
7190   cp->hTo = h;
7191   *pr = (ProcRef *) cp;
7192
7193   return NO_ERROR;
7194 }
7195
7196 int
7197 OpenLoopback(ProcRef *pr)
7198 {
7199   DisplayFatalError("Not implemented", 0, 1);
7200   return NO_ERROR;
7201 }
7202
7203
7204 int
7205 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
7206 {
7207   ChildProc *cp;
7208   int err;
7209   SOCKET s, s2, s3;
7210   struct sockaddr_in sa, mysa;
7211   struct hostent FAR *hp;
7212   unsigned short uport;
7213   WORD wVersionRequested;
7214   WSADATA wsaData;
7215   int fromPort;
7216   char stderrPortStr[MSG_SIZ];
7217
7218   /* Initialize socket DLL */
7219   wVersionRequested = MAKEWORD(1, 1);
7220   err = WSAStartup(wVersionRequested, &wsaData);
7221   if (err != 0) return err;
7222
7223   /* Resolve remote host name */
7224   memset((char *) &sa, 0, sizeof(struct sockaddr_in));
7225   if (!(hp = gethostbyname(host))) {
7226     unsigned int b0, b1, b2, b3;
7227
7228     err = WSAGetLastError();
7229
7230     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
7231       hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7232       hp->h_addrtype = AF_INET;
7233       hp->h_length = 4;
7234       hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7235       hp->h_addr_list[0] = (char *) malloc(4);
7236       hp->h_addr_list[0][0] = (char) b0;
7237       hp->h_addr_list[0][1] = (char) b1;
7238       hp->h_addr_list[0][2] = (char) b2;
7239       hp->h_addr_list[0][3] = (char) b3;
7240     } else {
7241       WSACleanup();
7242       return err;
7243     }
7244   }
7245   sa.sin_family = hp->h_addrtype;
7246   uport = (unsigned short) 514;
7247   sa.sin_port = htons(uport);
7248   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7249
7250   /* Bind local socket to unused "privileged" port address
7251    */
7252   s = INVALID_SOCKET;
7253   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7254   mysa.sin_family = AF_INET;
7255   mysa.sin_addr.s_addr = INADDR_ANY;
7256   for (fromPort = 1023;; fromPort--) {
7257     if (fromPort < 0) {
7258       WSACleanup();
7259       return WSAEADDRINUSE;
7260     }
7261     if (s == INVALID_SOCKET) {
7262       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7263         err = WSAGetLastError();
7264         WSACleanup();
7265         return err;
7266       }
7267     }
7268     uport = (unsigned short) fromPort;
7269     mysa.sin_port = htons(uport);
7270     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7271         == SOCKET_ERROR) {
7272       err = WSAGetLastError();
7273       if (err == WSAEADDRINUSE) continue;
7274       WSACleanup();
7275       return err;
7276     }
7277     if (connect(s, (struct sockaddr *) &sa,
7278       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
7279       err = WSAGetLastError();
7280       if (err == WSAEADDRINUSE) {
7281         closesocket(s);
7282         s = -1;
7283         continue;
7284       }
7285       WSACleanup();
7286       return err;
7287     }
7288     break;
7289   }
7290
7291   /* Bind stderr local socket to unused "privileged" port address
7292    */
7293   s2 = INVALID_SOCKET;
7294   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
7295   mysa.sin_family = AF_INET;
7296   mysa.sin_addr.s_addr = INADDR_ANY;
7297   for (fromPort = 1023;; fromPort--) {
7298     if (fromPort == prevStderrPort) continue; // don't reuse port
7299     if (fromPort < 0) {
7300       (void) closesocket(s);
7301       WSACleanup();
7302       return WSAEADDRINUSE;
7303     }
7304     if (s2 == INVALID_SOCKET) {
7305       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
7306         err = WSAGetLastError();
7307         closesocket(s);
7308         WSACleanup();
7309         return err;
7310       }
7311     }
7312     uport = (unsigned short) fromPort;
7313     mysa.sin_port = htons(uport);
7314     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
7315         == SOCKET_ERROR) {
7316       err = WSAGetLastError();
7317       if (err == WSAEADDRINUSE) continue;
7318       (void) closesocket(s);
7319       WSACleanup();
7320       return err;
7321     }
7322     if (listen(s2, 1) == SOCKET_ERROR) {
7323       err = WSAGetLastError();
7324       if (err == WSAEADDRINUSE) {
7325         closesocket(s2);
7326         s2 = INVALID_SOCKET;
7327         continue;
7328       }
7329       (void) closesocket(s);
7330       (void) closesocket(s2);
7331       WSACleanup();
7332       return err;
7333     }
7334     break;
7335   }
7336   prevStderrPort = fromPort; // remember port used
7337   sprintf(stderrPortStr, "%d", fromPort);
7338
7339   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
7340     err = WSAGetLastError();
7341     (void) closesocket(s);
7342     (void) closesocket(s2);
7343     WSACleanup();
7344     return err;
7345   }
7346
7347   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
7348     err = WSAGetLastError();
7349     (void) closesocket(s);
7350     (void) closesocket(s2);
7351     WSACleanup();
7352     return err;
7353   }
7354   if (*user == NULLCHAR) user = UserName();
7355   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
7356     err = WSAGetLastError();
7357     (void) closesocket(s);
7358     (void) closesocket(s2);
7359     WSACleanup();
7360     return err;
7361   }
7362   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
7363     err = WSAGetLastError();
7364     (void) closesocket(s);
7365     (void) closesocket(s2);
7366     WSACleanup();
7367     return err;
7368   }
7369
7370   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
7371     err = WSAGetLastError();
7372     (void) closesocket(s);
7373     (void) closesocket(s2);
7374     WSACleanup();
7375     return err;
7376   }
7377   (void) closesocket(s2);  /* Stop listening */
7378
7379   /* Prepare return value */
7380   cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7381   cp->kind = CPRcmd;
7382   cp->sock = s;
7383   cp->sock2 = s3;
7384   *pr = (ProcRef *) cp;
7385
7386   return NO_ERROR;
7387 }
7388
7389
7390 InputSourceRef
7391 AddInputSource(ProcRef pr, int lineByLine,
7392                InputCallback func, VOIDSTAR closure)
7393 {
7394   InputSource *is, *is2;
7395   ChildProc *cp = (ChildProc *) pr;
7396
7397   is = (InputSource *) calloc(1, sizeof(InputSource));
7398   is->lineByLine = lineByLine;
7399   is->func = func;
7400   is->closure = closure;
7401   is->second = NULL;
7402   is->next = is->buf;
7403   if (pr == NoProc) {
7404     is->kind = CPReal;
7405     consoleInputSource = is;
7406   } else {
7407     is->kind = cp->kind;
7408     switch (cp->kind) {
7409     case CPReal:
7410       is->hFile = cp->hFrom;
7411       cp->hFrom = NULL; /* now owned by InputThread */
7412       is->hThread =
7413         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
7414                      (LPVOID) is, 0, &is->id);
7415       break;
7416
7417     case CPComm:
7418       is->hFile = cp->hFrom;
7419       cp->hFrom = NULL; /* now owned by InputThread */
7420       is->hThread =
7421         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
7422                      (LPVOID) is, 0, &is->id);
7423       break;
7424
7425     case CPSock:
7426       is->sock = cp->sock;
7427       is->hThread =
7428         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7429                      (LPVOID) is, 0, &is->id);
7430       break;
7431
7432     case CPRcmd:
7433       is2 = (InputSource *) calloc(1, sizeof(InputSource));
7434       *is2 = *is;
7435       is->sock = cp->sock;
7436       is->second = is2;
7437       is2->sock = cp->sock2;
7438       is2->second = is2;
7439       is->hThread =
7440         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7441                      (LPVOID) is, 0, &is->id);
7442       is2->hThread =
7443         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
7444                      (LPVOID) is2, 0, &is2->id);
7445       break;
7446     }
7447   }
7448   return (InputSourceRef) is;
7449 }
7450
7451 void
7452 RemoveInputSource(InputSourceRef isr)
7453 {
7454   InputSource *is;
7455
7456   is = (InputSource *) isr;
7457   is->hThread = NULL;  /* tell thread to stop */
7458   CloseHandle(is->hThread);
7459   if (is->second != NULL) {
7460     is->second->hThread = NULL;
7461     CloseHandle(is->second->hThread);
7462   }
7463 }
7464
7465
7466 int
7467 OutputToProcess(ProcRef pr, char *message, int count, int *outError)
7468 {
7469   DWORD dOutCount;
7470   int outCount = SOCKET_ERROR;
7471   ChildProc *cp = (ChildProc *) pr;
7472   static OVERLAPPED ovl;
7473
7474   if (pr == NoProc) {
7475     ConsoleOutput(message, count, FALSE);
7476     return count;
7477   } 
7478
7479   if (ovl.hEvent == NULL) {
7480     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
7481   }
7482   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
7483
7484   switch (cp->kind) {
7485   case CPSock:
7486   case CPRcmd:
7487     outCount = send(cp->sock, message, count, 0);
7488     if (outCount == SOCKET_ERROR) {
7489       *outError = WSAGetLastError();
7490     } else {
7491       *outError = NO_ERROR;
7492     }
7493     break;
7494
7495   case CPReal:
7496     if (WriteFile(((ChildProc *)pr)->hTo, message, count,
7497                   &dOutCount, NULL)) {
7498       *outError = NO_ERROR;
7499       outCount = (int) dOutCount;
7500     } else {
7501       *outError = GetLastError();
7502     }
7503     break;
7504
7505   case CPComm:
7506     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
7507                             &dOutCount, &ovl);
7508     if (*outError == NO_ERROR) {
7509       outCount = (int) dOutCount;
7510     }
7511     break;
7512   }
7513   return outCount;
7514 }
7515
7516 int
7517 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
7518                        long msdelay)
7519 {
7520   /* Ignore delay, not implemented for WinBoard */
7521   return OutputToProcess(pr, message, count, outError);
7522 }
7523
7524
7525 void
7526 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
7527                         char *buf, int count, int error)
7528 {
7529   DisplayFatalError("Not implemented", 0, 1);
7530 }
7531
7532 /* see wgamelist.c for Game List functions */
7533 /* see wedittags.c for Edit Tags functions */
7534
7535
7536 VOID
7537 ICSInitScript()
7538 {
7539   FILE *f;
7540   char buf[MSG_SIZ];
7541   char *dummy;
7542
7543   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
7544     f = fopen(buf, "r");
7545     if (f != NULL) {
7546       ProcessICSInitScript(f);
7547       fclose(f);
7548     }
7549   }
7550 }
7551
7552
7553 VOID
7554 StartAnalysisClock()
7555 {
7556   if (analysisTimerEvent) return;
7557   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
7558                                         (UINT) 2000, NULL);
7559 }
7560
7561 LRESULT CALLBACK
7562 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
7563 {
7564   static HANDLE hwndText;
7565   RECT rect;
7566   static int sizeX, sizeY;
7567   int newSizeX, newSizeY, flags;
7568   MINMAXINFO *mmi;
7569
7570   switch (message) {
7571   case WM_INITDIALOG: /* message: initialize dialog box */
7572     /* Initialize the dialog items */
7573     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);
7574     SetWindowText(hDlg, analysisTitle);
7575     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);
7576     /* Size and position the dialog */
7577     if (!analysisDialog) {
7578       analysisDialog = hDlg;
7579       flags = SWP_NOZORDER;
7580       GetClientRect(hDlg, &rect);
7581       sizeX = rect.right;
7582       sizeY = rect.bottom;
7583       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&
7584           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {
7585         WINDOWPLACEMENT wp;
7586         EnsureOnScreen(&analysisX, &analysisY);
7587         wp.length = sizeof(WINDOWPLACEMENT);
7588         wp.flags = 0;
7589         wp.showCmd = SW_SHOW;
7590         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
7591         wp.rcNormalPosition.left = analysisX;
7592         wp.rcNormalPosition.right = analysisX + analysisW;
7593         wp.rcNormalPosition.top = analysisY;
7594         wp.rcNormalPosition.bottom = analysisY + analysisH;
7595         SetWindowPlacement(hDlg, &wp);
7596
7597         GetClientRect(hDlg, &rect);
7598         newSizeX = rect.right;
7599         newSizeY = rect.bottom;
7600         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
7601                               newSizeX, newSizeY);
7602         sizeX = newSizeX;
7603         sizeY = newSizeY;
7604       }
7605     }
7606     return FALSE;
7607
7608   case WM_COMMAND: /* message: received a command */
7609     switch (LOWORD(wParam)) {
7610     case IDCANCEL:
7611       EditGameEvent();
7612       return TRUE;
7613     default:
7614       break;
7615     }
7616     break;
7617
7618   case WM_SIZE:
7619     newSizeX = LOWORD(lParam);
7620     newSizeY = HIWORD(lParam);
7621     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
7622     sizeX = newSizeX;
7623     sizeY = newSizeY;
7624     break;
7625
7626   case WM_GETMINMAXINFO:
7627     /* Prevent resizing window too small */
7628     mmi = (MINMAXINFO *) lParam;
7629     mmi->ptMinTrackSize.x = 100;
7630     mmi->ptMinTrackSize.y = 100;
7631     break;
7632   }
7633   return FALSE;
7634 }
7635
7636 VOID
7637 AnalysisPopUp(char* title, char* str)
7638 {
7639   FARPROC lpProc;
7640   char *p, *q;
7641
7642   if (str == NULL) str = "";
7643   p = (char *) malloc(2 * strlen(str) + 2);
7644   q = p;
7645   while (*str) {
7646     if (*str == '\n') *q++ = '\r';
7647     *q++ = *str++;
7648   }
7649   *q = NULLCHAR;
7650   if (analysisText != NULL) free(analysisText);
7651   analysisText = p;
7652
7653   if (analysisDialog) {
7654     SetWindowText(analysisDialog, title);
7655     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);
7656     ShowWindow(analysisDialog, SW_SHOW);
7657   } else {
7658     analysisTitle = title;
7659     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);
7660     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),
7661                  hwndMain, (DLGPROC)lpProc);
7662     FreeProcInstance(lpProc);
7663   }
7664   analysisDialogUp = TRUE;  
7665 }
7666
7667 VOID
7668 AnalysisPopDown()
7669 {
7670   if (analysisDialog) {
7671     ShowWindow(analysisDialog, SW_HIDE);
7672   }
7673   analysisDialogUp = FALSE;  
7674 }
7675
7676
7677 VOID
7678 SetHighlights(int fromX, int fromY, int toX, int toY)
7679 {
7680   highlightInfo.sq[0].x = fromX;
7681   highlightInfo.sq[0].y = fromY;
7682   highlightInfo.sq[1].x = toX;
7683   highlightInfo.sq[1].y = toY;
7684 }
7685
7686 VOID
7687 ClearHighlights()
7688 {
7689   highlightInfo.sq[0].x = highlightInfo.sq[0].y = 
7690     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
7691 }
7692
7693 VOID
7694 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
7695 {
7696   premoveHighlightInfo.sq[0].x = fromX;
7697   premoveHighlightInfo.sq[0].y = fromY;
7698   premoveHighlightInfo.sq[1].x = toX;
7699   premoveHighlightInfo.sq[1].y = toY;
7700 }
7701
7702 VOID
7703 ClearPremoveHighlights()
7704 {
7705   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = 
7706     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
7707 }
7708
7709 VOID
7710 ShutDownFrontEnd()
7711 {
7712   if (saveSettingsOnExit) SaveSettings(settingsFileName);
7713   DeleteClipboardTempFiles();
7714 }
7715
7716 void
7717 BoardToTop()
7718 {
7719     if (IsIconic(hwndMain))
7720       ShowWindow(hwndMain, SW_RESTORE);
7721
7722     SetActiveWindow(hwndMain);
7723 }
7724
7725 /*
7726  * Prototypes for animation support routines
7727  */
7728 static void ScreenSquare(int column, int row, POINT * pt);
7729 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
7730      POINT frames[], int * nFrames);
7731
7732
7733 #define kFactor 4
7734
7735 void
7736 AnimateMove(board, fromX, fromY, toX, toY)
7737      Board board;
7738      int fromX;
7739      int fromY;
7740      int toX;
7741      int toY;
7742 {
7743   ChessSquare piece;
7744   POINT start, finish, mid;
7745   POINT frames[kFactor * 2 + 1];
7746   int nFrames, n;
7747
7748   if (!appData.animate) return;
7749   if (doingSizing) return;
7750   if (fromY < 0 || fromX < 0) return;
7751   piece = board[fromY][fromX];
7752   if (piece >= EmptySquare) return;
7753
7754   ScreenSquare(fromX, fromY, &start);
7755   ScreenSquare(toX, toY, &finish);
7756
7757   /* All pieces except knights move in straight line */
7758   if (piece != WhiteKnight && piece != BlackKnight) {
7759     mid.x = start.x + (finish.x - start.x) / 2;
7760     mid.y = start.y + (finish.y - start.y) / 2;
7761   } else {
7762     /* Knight: make diagonal movement then straight */
7763     if (abs(toY - fromY) < abs(toX - fromX)) {
7764        mid.x = start.x + (finish.x - start.x) / 2;
7765        mid.y = finish.y;
7766      } else {
7767        mid.x = finish.x;
7768        mid.y = start.y + (finish.y - start.y) / 2;
7769      }
7770   }
7771   
7772   /* Don't use as many frames for very short moves */
7773   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7774     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7775   else
7776     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7777
7778   animInfo.from.x = fromX;
7779   animInfo.from.y = fromY;
7780   animInfo.to.x = toX;
7781   animInfo.to.y = toY;
7782   animInfo.lastpos = start;
7783   animInfo.piece = piece;
7784   for (n = 0; n < nFrames; n++) {
7785     animInfo.pos = frames[n];
7786     DrawPosition(FALSE, NULL);
7787     animInfo.lastpos = animInfo.pos;
7788     Sleep(appData.animSpeed);
7789   }
7790   animInfo.pos = finish;
7791   DrawPosition(FALSE, NULL);
7792   animInfo.piece = EmptySquare;
7793 }
7794
7795 /*      Convert board position to corner of screen rect and color       */
7796
7797 static void
7798 ScreenSquare(column, row, pt)
7799      int column; int row; POINT * pt;
7800 {
7801   if (flipView) {
7802     pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
7803     pt->y = lineGap + row * (squareSize + lineGap);
7804   } else {
7805     pt->x = lineGap + column * (squareSize + lineGap);
7806     pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
7807   }
7808 }
7809
7810 /*      Generate a series of frame coords from start->mid->finish.
7811         The movement rate doubles until the half way point is
7812         reached, then halves back down to the final destination,
7813         which gives a nice slow in/out effect. The algorithmn
7814         may seem to generate too many intermediates for short
7815         moves, but remember that the purpose is to attract the
7816         viewers attention to the piece about to be moved and
7817         then to where it ends up. Too few frames would be less
7818         noticeable.                                             */
7819
7820 static void
7821 Tween(start, mid, finish, factor, frames, nFrames)
7822      POINT * start; POINT * mid;
7823      POINT * finish; int factor;
7824      POINT frames[]; int * nFrames;
7825 {
7826   int n, fraction = 1, count = 0;
7827
7828   /* Slow in, stepping 1/16th, then 1/8th, ... */
7829   for (n = 0; n < factor; n++)
7830     fraction *= 2;
7831   for (n = 0; n < factor; n++) {
7832     frames[count].x = start->x + (mid->x - start->x) / fraction;
7833     frames[count].y = start->y + (mid->y - start->y) / fraction;
7834     count ++;
7835     fraction = fraction / 2;
7836   }
7837   
7838   /* Midpoint */
7839   frames[count] = *mid;
7840   count ++;
7841   
7842   /* Slow out, stepping 1/2, then 1/4, ... */
7843   fraction = 2;
7844   for (n = 0; n < factor; n++) {
7845     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
7846     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
7847     count ++;
7848     fraction = fraction * 2;
7849   }
7850   *nFrames = count;
7851 }
7852
7853 void
7854 HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
7855 {
7856   /* Currently not implemented in WinBoard */
7857 }
7858
7859