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