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