MS SDK >= feb. 2003
[xboard.git] / winboard / winboard.c
1 /* 
2  * WinBoard.c -- Windows NT front end to XBoard
3  * $Id$
4  *
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
7  *
8  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
9  * which was written and is copyrighted by Wayne Christopher.
10  *
11  * The following terms apply to Digital Equipment Corporation's copyright
12  * interest in XBoard:
13  * ------------------------------------------------------------------------
14  * All Rights Reserved
15  *
16  * Permission to use, copy, modify, and distribute this software and its
17  * documentation for any purpose and without fee is hereby granted,
18  * provided that the above copyright notice appear in all copies and that
19  * both that copyright notice and this permission notice appear in
20  * supporting documentation, and that the name of Digital not be
21  * used in advertising or publicity pertaining to distribution of the
22  * software without specific, written prior permission.
23  *
24  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
25  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
26  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
27  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
28  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
29  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30  * SOFTWARE.
31  * ------------------------------------------------------------------------
32  *
33  * The following terms apply to the enhanced version of XBoard distributed
34  * by the Free Software Foundation:
35  * ------------------------------------------------------------------------
36  * This program is free software; you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation; either version 2 of the License, or
39  * (at your option) any later version.
40  *
41  * This program is distributed in the hope that it will be useful,
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44  * GNU General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program; if not, write to the Free Software
48  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
49  * ------------------------------------------------------------------------
50  */
51
52 #include "config.h"
53
54 #include <windows.h>
55 #include <winuser.h>
56 #include <winsock.h>
57
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <malloc.h>
61 #include <sys/stat.h>
62 #include <fcntl.h>
63 #include <math.h>
64 #include <commdlg.h>
65 #include <dlgs.h>
66 #include <richedit.h>
67 #include <mmsystem.h>
68
69 #if __GNUC__
70 #include <errno.h>
71 #include <string.h>
72 #endif
73
74 #include "common.h"
75 #include "winboard.h"
76 #include "frontend.h"
77 #include "backend.h"
78 #include "moves.h"
79 #include "wclipbrd.h"
80 #include "wgamelist.h"
81 #include "wedittags.h"
82 #include "woptions.h"
83 #include "wsockerr.h"
84 #include "defaults.h"
85
86 typedef struct {
87   ChessSquare piece;  
88   POINT pos;      /* window coordinates of current pos */
89   POINT lastpos;  /* window coordinates of last pos - used for clipping */
90   POINT from;     /* board coordinates of the piece's orig pos */
91   POINT to;       /* board coordinates of the piece's new pos */
92 } AnimInfo;
93
94 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
95
96 typedef struct {
97   POINT start;    /* window coordinates of start pos */
98   POINT pos;      /* window coordinates of current pos */
99   POINT lastpos;  /* window coordinates of last pos - used for clipping */
100   POINT from;     /* board coordinates of the piece's orig pos */
101 } DragInfo;
102
103 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
104
105 typedef struct {
106   POINT sq[2];    /* board coordinates of from, to squares */
107 } HighlightInfo;
108
109 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };
110 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
111
112 /* Window class names */
113 char szAppName[] = "WinBoard";
114 char szConsoleName[] = "WBConsole";
115
116 /* Title bar text */
117 char szTitle[] = "WinBoard";
118 char szConsoleTitle[] = "ICS Interaction";
119
120 char *programName;
121 char *settingsFileName;
122 BOOLEAN saveSettingsOnExit;
123 char installDir[MSG_SIZ];
124
125 BoardSize boardSize;
126 BOOLEAN chessProgram;
127 static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;
128 static int squareSize, lineGap;
129 static int winWidth, winHeight;
130 static RECT messageRect, whiteRect, blackRect;
131 static char messageText[MESSAGE_TEXT_MAX];
132 static int clockTimerEvent = 0;
133 static int loadGameTimerEvent = 0;
134 static int analysisTimerEvent = 0;
135 static DelayedEventCallback delayedTimerCallback;
136 static int delayedTimerEvent = 0;
137 static int buttonCount = 2;
138 char *icsTextMenuString;
139 char *icsNames;
140 char *firstChessProgramNames;
141 char *secondChessProgramNames;
142
143 #define ARG_MAX 20000
144
145 #define PALETTESIZE 256
146
147 HINSTANCE hInst;          /* current instance */
148 HWND hwndMain = NULL;        /* root window*/
149 HWND hwndConsole = NULL;
150 BOOLEAN alwaysOnTop = FALSE;
151 RECT boardRect;
152 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, 
153   blackPieceColor, highlightSquareColor, premoveHighlightColor;
154 HPALETTE hPal;
155 ColorClass currentColorClass;
156
157 HWND hCommPort = NULL;    /* currently open comm port */
158 static HWND hwndPause;    /* pause button */
159 static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];
160 static HBRUSH lightSquareBrush, darkSquareBrush,
161   whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;
162 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];
163 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];
164 static HPEN gridPen = NULL;
165 static HPEN highlightPen = NULL;
166 static HPEN premovePen = NULL;
167 static NPLOGPALETTE pLogPal;
168 static BOOL paletteChanged = FALSE;
169 static HICON iconWhite, iconBlack, iconCurrent;
170 static int doingSizing = FALSE;
171 static int lastSizing = 0;
172 static int prevStderrPort;
173
174 #if __GNUC__ && !defined(_winmajor)
175 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
176 #else
177 #define oldDialog (_winmajor < 4)
178 #endif
179
180 char *defaultTextAttribs[] = 
181 {
182   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,
183   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,
184   COLOR_NONE
185 };
186
187 typedef struct {
188   char *name;
189   int squareSize;
190   int lineGap;
191   int smallLayout;
192   int tinyLayout;
193   int cliWidth, cliHeight;
194 } SizeInfo;
195
196 SizeInfo sizeInfo[] = 
197 {
198   { "tiny",     21, 0, 1, 1, 0, 0 },
199   { "teeny",    25, 1, 1, 1, 0, 0 },
200   { "dinky",    29, 1, 1, 1, 0, 0 },
201   { "petite",   33, 1, 1, 1, 0, 0 },
202   { "slim",     37, 2, 1, 0, 0, 0 },
203   { "small",    40, 2, 1, 0, 0, 0 },
204   { "mediocre", 45, 2, 1, 0, 0, 0 },
205   { "middling", 49, 2, 0, 0, 0, 0 },
206   { "average",  54, 2, 0, 0, 0, 0 },
207   { "moderate", 58, 3, 0, 0, 0, 0 },
208   { "medium",   64, 3, 0, 0, 0, 0 },
209   { "bulky",    72, 3, 0, 0, 0, 0 },
210   { "large",    80, 3, 0, 0, 0, 0 },
211   { "big",      87, 3, 0, 0, 0, 0 },
212   { "huge",     95, 3, 0, 0, 0, 0 },
213   { "giant",    108, 3, 0, 0, 0, 0 },
214   { "colossal", 116, 4, 0, 0, 0, 0 },
215   { "titanic",  129, 4, 0, 0, 0, 0 },
216   { NULL, 0, 0, 0, 0, 0, 0 }
217 };
218
219 #define MF(x) {x, {0, }, {0, }, 0}
220 MyFont fontRec[NUM_SIZES][NUM_FONTS] =
221 {
222   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), 
223     MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),
224     MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },
225   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), 
226     MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),
227     MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },
228   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),
229     MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),
230     MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },
231   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),
232     MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),
233     MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },
234   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),
235     MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),
236     MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },
237   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),
238     MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),
239     MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },
240   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),
241     MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),
242     MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },
243   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),
244     MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),
245     MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },
246   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),
247     MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),
248     MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },
249   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),
250     MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),
251     MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },
252   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),
253     MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),
254     MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },
255   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),
256     MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),
257     MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },
258   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),
259     MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),
260     MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },
261   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),
262     MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),
263     MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },
264   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),
265     MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),
266     MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },
267   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),
268     MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),
269     MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },
270   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),
271     MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),
272     MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },
273   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),
274     MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),
275     MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },
276 };
277
278 MyFont *font[NUM_SIZES][NUM_FONTS];
279
280 typedef struct {
281   char *label;
282   int id;
283   HWND hwnd;
284   WNDPROC wndproc;
285 } MyButtonDesc;
286
287 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)
288 #define N_BUTTONS 5
289
290 MyButtonDesc buttonDesc[N_BUTTONS] =
291 {
292   {"<<", IDM_ToStart, NULL, NULL},
293   {"<", IDM_Backward, NULL, NULL},
294   {"P", IDM_Pause, NULL, NULL},
295   {">", IDM_Forward, NULL, NULL},
296   {">>", IDM_ToEnd, NULL, NULL},
297 };
298
299 int tinyLayout = 0, smallLayout = 0;
300 #define MENU_BAR_ITEMS 6
301 char *menuBarText[2][MENU_BAR_ITEMS+1] = {
302   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
303   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
304 };
305
306
307 MySound sounds[(int)NSoundClasses];
308 MyTextAttribs textAttribs[(int)NColorClasses];
309
310 MyColorizeAttribs colorizeAttribs[] = {
311   { (COLORREF)0, 0, "Shout Text" },
312   { (COLORREF)0, 0, "SShout/CShout" },
313   { (COLORREF)0, 0, "Channel 1 Text" },
314   { (COLORREF)0, 0, "Channel Text" },
315   { (COLORREF)0, 0, "Kibitz Text" },
316   { (COLORREF)0, 0, "Tell Text" },
317   { (COLORREF)0, 0, "Challenge Text" },
318   { (COLORREF)0, 0, "Request Text" },
319   { (COLORREF)0, 0, "Seek Text" },
320   { (COLORREF)0, 0, "Normal Text" },
321   { (COLORREF)0, 0, "None" }
322 };
323
324
325
326 static char *commentTitle;
327 static char *commentText;
328 static int commentIndex;
329 static Boolean editComment = FALSE;
330 HWND commentDialog = NULL;
331 BOOLEAN commentDialogUp = FALSE;
332 static int commentX, commentY, commentH, commentW;
333
334 static char *analysisTitle;
335 static char *analysisText;
336 HWND analysisDialog = NULL;
337 BOOLEAN analysisDialogUp = FALSE;
338 static int analysisX, analysisY, analysisH, analysisW;
339
340 char errorTitle[MSG_SIZ];
341 char errorMessage[2*MSG_SIZ];
342 HWND errorDialog = NULL;
343 BOOLEAN moveErrorMessageUp = FALSE;
344 BOOLEAN consoleEcho = TRUE;
345 CHARFORMAT consoleCF;
346 COLORREF consoleBackgroundColor;
347
348 char *programVersion;
349
350 #define CPReal 1
351 #define CPComm 2
352 #define CPSock 3
353 #define CPRcmd 4
354 typedef int CPKind;
355
356 typedef struct {
357   CPKind kind;
358   HANDLE hProcess;
359   DWORD pid;
360   HANDLE hTo;
361   HANDLE hFrom;
362   SOCKET sock;
363   SOCKET sock2;  /* stderr socket for OpenRcmd */
364 } ChildProc;
365
366 #define INPUT_SOURCE_BUF_SIZE 4096
367
368 typedef struct _InputSource {
369   CPKind kind;
370   HANDLE hFile;
371   SOCKET sock;
372   int lineByLine;
373   HANDLE hThread;
374   DWORD id;
375   char buf[INPUT_SOURCE_BUF_SIZE];
376   char *next;
377   DWORD count;
378   int error;
379   InputCallback func;
380   struct _InputSource *second;  /* for stderr thread on CPRcmd */
381   VOIDSTAR closure;
382 } InputSource;
383
384 InputSource *consoleInputSource;
385
386 DCB dcb;
387
388 /* forward */
389 VOID ConsoleOutput(char* data, int length, int forceVisible);
390 VOID ConsoleCreate();
391 LRESULT CALLBACK
392   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
393 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
394 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
395 VOID ParseCommSettings(char *arg, DCB *dcb);
396 LRESULT CALLBACK
397   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
398 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
399 void ParseIcsTextMenu(char *icsTextMenuString);
400 VOID PopUpMoveDialog(char firstchar);
401 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
402
403 /*
404  * Setting "frozen" should disable all user input other than deleting
405  * the window.  We do this while engines are initializing themselves.
406  */
407 static int frozen = 0;
408 static int oldMenuItemState[MENU_BAR_ITEMS];
409 void FreezeUI()
410 {
411   HMENU hmenu;
412   int i;
413
414   if (frozen) return;
415   frozen = 1;
416   hmenu = GetMenu(hwndMain);
417   for (i=0; i<MENU_BAR_ITEMS; i++) {
418     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
419   }
420   DrawMenuBar(hwndMain);
421 }
422
423 /* Undo a FreezeUI */
424 void ThawUI()
425 {
426   HMENU hmenu;
427   int i;
428
429   if (!frozen) return;
430   frozen = 0;
431   hmenu = GetMenu(hwndMain);
432   for (i=0; i<MENU_BAR_ITEMS; i++) {
433     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
434   }
435   DrawMenuBar(hwndMain);
436 }
437
438 /*---------------------------------------------------------------------------*\
439  *
440  * WinMain
441  *
442 \*---------------------------------------------------------------------------*/
443
444 int APIENTRY
445 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
446         LPSTR lpCmdLine, int nCmdShow)
447 {
448   MSG msg;
449   HANDLE hAccelMain, hAccelNoAlt;
450
451   debugFP = stderr;
452
453   LoadLibrary("RICHED32.DLL");
454   consoleCF.cbSize = sizeof(CHARFORMAT);
455
456   if (!InitApplication(hInstance)) {
457     return (FALSE);
458   }
459   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
460     return (FALSE);
461   }
462
463   hAccelMain = LoadAccelerators (hInstance, szAppName);
464   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
465
466   /* Acquire and dispatch messages until a WM_QUIT message is received. */
467
468   while (GetMessage(&msg, /* message structure */
469                     NULL, /* handle of window receiving the message */
470                     0,    /* lowest message to examine */
471                     0))   /* highest message to examine */
472     {
473       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
474           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
475           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
476           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
477           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&
478           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
479         TranslateMessage(&msg); /* Translates virtual key codes */
480         DispatchMessage(&msg);  /* Dispatches message to window */
481       }
482     }
483
484
485   return (msg.wParam);  /* Returns the value from PostQuitMessage */
486 }
487
488 /*---------------------------------------------------------------------------*\
489  *
490  * Initialization functions
491  *
492 \*---------------------------------------------------------------------------*/
493
494 BOOL
495 InitApplication(HINSTANCE hInstance)
496 {
497   WNDCLASS wc;
498
499   /* Fill in window class structure with parameters that describe the */
500   /* main window. */
501
502   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
503   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */
504   wc.cbClsExtra    = 0;                 /* No per-class extra data. */
505   wc.cbWndExtra    = 0;                 /* No per-window extra data. */
506   wc.hInstance     = hInstance;         /* Owner of this class */
507   wc.hIcon         = LoadIcon(hInstance, "icon_white");
508   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */
509   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */
510   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */
511   wc.lpszClassName = szAppName;                 /* Name to register as */
512
513   /* Register the window class and return success/failure code. */
514   if (!RegisterClass(&wc)) return FALSE;
515
516   wc.style         = CS_HREDRAW | CS_VREDRAW;
517   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;
518   wc.cbClsExtra    = 0;
519   wc.cbWndExtra    = DLGWINDOWEXTRA;
520   wc.hInstance     = hInstance;
521   wc.hIcon         = LoadIcon(hInstance, "icon_white");
522   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
523   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
524   wc.lpszMenuName  = NULL;
525   wc.lpszClassName = szConsoleName;
526
527   if (!RegisterClass(&wc)) return FALSE;
528   return TRUE;
529 }
530
531
532 /* Set by InitInstance, used by EnsureOnScreen */
533 int screenHeight, screenWidth;
534
535 void
536 EnsureOnScreen(int *x, int *y)
537 {
538   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
539   if (*x > screenWidth - 32) *x = 0;
540   if (*y > screenHeight - 32) *y = 0;
541 }
542
543 BOOL
544 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
545 {
546   HWND hwnd; /* Main window handle. */
547   int ibs;
548   WINDOWPLACEMENT wp;
549   char *filepart;
550
551   hInst = hInstance;    /* Store instance handle in our global variable */
552
553   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
554     *filepart = NULLCHAR;
555   } else {
556     GetCurrentDirectory(MSG_SIZ, installDir);
557   }
558   InitAppData(lpCmdLine);      /* Get run-time parameters */
559   if (appData.debugMode) {
560     debugFP = fopen("winboard.debug", "w");
561     setbuf(debugFP, NULL);
562   }
563
564   InitBackEnd1();
565
566   /* Create a main window for this application instance. */
567   hwnd = CreateWindow(szAppName, szTitle,
568                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
569                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
570                       NULL, NULL, hInstance, NULL);
571   hwndMain = hwnd;
572
573   /* If window could not be created, return "failure" */
574   if (!hwnd) {
575     return (FALSE);
576   }
577
578   iconWhite = LoadIcon(hInstance, "icon_white");
579   iconBlack = LoadIcon(hInstance, "icon_black");
580   iconCurrent = iconWhite;
581   InitDrawingColors();
582   screenHeight = GetSystemMetrics(SM_CYSCREEN);
583   screenWidth = GetSystemMetrics(SM_CXSCREEN);
584   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
585     /* Compute window size for each board size, and use the largest
586        size that fits on this screen as the default. */
587     InitDrawingSizes((BoardSize)ibs, 0);
588     if (boardSize == (BoardSize)-1 &&
589         winHeight <= screenHeight && winWidth <= screenWidth) {
590       boardSize = (BoardSize)ibs;
591     }
592   }
593   InitDrawingSizes(boardSize, 0);
594   InitMenuChecks();
595   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
596
597   InitBackEnd2();
598
599   /* Make the window visible; update its client area; and return "success" */
600   EnsureOnScreen(&boardX, &boardY);
601   wp.length = sizeof(WINDOWPLACEMENT);
602   wp.flags = 0;
603   wp.showCmd = nCmdShow;
604   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
605   wp.rcNormalPosition.left = boardX;
606   wp.rcNormalPosition.right = boardX + winWidth;
607   wp.rcNormalPosition.top = boardY;
608   wp.rcNormalPosition.bottom = boardY + winHeight;
609   SetWindowPlacement(hwndMain, &wp);
610
611   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
612                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
613   if (hwndConsole) {
614 #if AOT_CONSOLE
615     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
616                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
617 #endif
618     ShowWindow(hwndConsole, nCmdShow);
619   }
620   UpdateWindow(hwnd);
621
622   return TRUE;
623
624 }
625
626
627 typedef enum {
628   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, 
629   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,
630   ArgSettingsFilename
631 } ArgType;
632
633 typedef struct {
634   char *argName;
635   ArgType argType;
636   /***
637   union {
638     String *pString;       // ArgString
639     int *pInt;             // ArgInt
640     float *pFloat;         // ArgFloat
641     Boolean *pBoolean;     // ArgBoolean
642     COLORREF *pColor;      // ArgColor
643     ColorClass cc;         // ArgAttribs
644     String *pFilename;     // ArgFilename
645     BoardSize *pBoardSize; // ArgBoardSize
646     int whichFont;         // ArgFont
647     DCB *pDCB;             // ArgCommSettings
648     String *pFilename;     // ArgSettingsFilename
649   } argLoc;
650   ***/
651   LPVOID argLoc;
652   BOOL save;
653 } ArgDescriptor;
654
655 int junk;
656 ArgDescriptor argDescriptors[] = {
657   /* positional arguments */
658   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
659   { "", ArgNone, NULL },
660   /* keyword arguments */
661   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },
662   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },
663   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },
664   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },
665   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },
666   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },
667   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },
668   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },
669   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },
670   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },
671   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },
672   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },
673   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },
674   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },
675   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },
676   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },
677   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },
678   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,
679     FALSE },
680   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,
681     FALSE },
682   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,
683     FALSE },
684   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },
685   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,
686     FALSE },
687   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },
688   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },
689   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },
690   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
691   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
692   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },
693   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },
694   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
695   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
696   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },
697   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },
698   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },
699   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },
700   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
701   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
702   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
703   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
704   /*!!bitmapDirectory?*/
705   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
706   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
707   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
708   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
709   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },
710   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },
711   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },
712   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },
713   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },
714   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },
715   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },
716   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },
717   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
718   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
719   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },
720   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },
721   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },
722   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },
723   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
724   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
725   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
726   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
727   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
728   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
729   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },
730   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },
731   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
732   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
733   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },
734   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },
735   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },
736   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
737   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
738   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
739   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
740   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },
741   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },
742   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },
743   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },
744   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
745   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
746   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
747   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
748   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
749   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
750   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
751   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
752   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },
753   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },
754   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
755   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
756   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },
757   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },
758   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },
759   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },
760   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
761   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
762   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },
763   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },
764   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
765   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
766   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },
767   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },
768   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
769   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
770   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },
771   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },
772   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
773   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
774   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },
775   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },
776   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
777   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
778   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },
779   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },
780   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
781   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
782   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },
783   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },
784   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
785   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
786   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },
787   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },
788   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
789   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
790   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },
791   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },
792   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
793   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
794   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },
795   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },
796   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
797   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
798   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, 
799     FALSE }, /* only so that old WinBoard.ini files from betas can be read */
800   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },
801   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },
802   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },
803   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },
804   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },
805   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },
806   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,
807     TRUE }, /* must come after all fonts */
808   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },
809   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,
810     FALSE }, /* historical; kept only so old winboard.ini files will parse */
811   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },
812   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },
813   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
814   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
815   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },
816   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },
817   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
818   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
819   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },
820   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },
821   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
822   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
823   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },
824   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },
825   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
826   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
827   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },
828   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },
829   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
830   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
831   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },
832   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },
833   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
834   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
835   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },
836   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },
837   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
838   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
839 #if 0
840   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
841   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
842 #endif
843   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },
844   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
845   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
846   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
847   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },
848   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },
849   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
850   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
851   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },
852   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },
853   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
854   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
855   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },
856   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },
857   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
858   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
859   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },
860   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },
861   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
862   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
863   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },
864   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },
865   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },
866   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },
867   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },
868   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },
869   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
870   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
871   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },
872   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },
873   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },
874   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
875   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
876   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },
877   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},
878   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},
879   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
880   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
881   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},
882   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
883   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
884   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },
885   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
886   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
887   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },
888   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },
889   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },
890   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },
891   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },
892   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },
893   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },
894   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
895   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
896   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },
897   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },
898   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
899   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
900   { "highlightLastMove", ArgBoolean,
901     (LPVOID) &appData.highlightLastMove, TRUE },
902   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },
903   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
904   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
905   { "highlightDragging", ArgBoolean,
906     (LPVOID) &appData.highlightDragging, TRUE },
907   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },
908   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
909   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
910   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },
911   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },
912   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
913   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
914   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },
915   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },
916   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },
917   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },
918   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },
919   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },
920   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },
921   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },
922   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },
923   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },
924   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },
925   { "soundShout", ArgFilename,
926     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },
927   { "soundSShout", ArgFilename,
928     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },
929   { "soundChannel1", ArgFilename,
930     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },
931   { "soundChannel", ArgFilename,
932     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },
933   { "soundKibitz", ArgFilename,
934     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },
935   { "soundTell", ArgFilename,
936     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },
937   { "soundChallenge", ArgFilename,
938     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },
939   { "soundRequest", ArgFilename,
940     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },
941   { "soundSeek", ArgFilename,
942     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },
943   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },
944   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },
945   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },
946   { "soundIcsLoss", ArgFilename, 
947     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },
948   { "soundIcsDraw", ArgFilename, 
949     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },
950   { "soundIcsUnfinished", ArgFilename, 
951     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},
952   { "soundIcsAlarm", ArgFilename, 
953     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },
954   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },
955   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },
956   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
957   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
958   { "reuseChessPrograms", ArgBoolean,
959     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */
960   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },
961   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },
962   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
963   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
964   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },
965   { "x", ArgInt, (LPVOID) &boardX, TRUE },
966   { "y", ArgInt, (LPVOID) &boardY, TRUE },
967   { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },
968   { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },
969   { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },
970   { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },
971   { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },
972   { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },
973   { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },
974   { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },
975   { "commentX", ArgInt, (LPVOID) &commentX, TRUE },
976   { "commentY", ArgInt, (LPVOID) &commentY, TRUE },
977   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },
978   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },
979   { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },
980   { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },
981   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },
982   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },
983   { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },
984   { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },
985   { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },
986   { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },
987   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
988   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
989   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },
990   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },
991   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },
992   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },
993   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },
994   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },
995   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },
996   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,
997     TRUE },
998   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,
999     TRUE },
1000   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1001   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },
1002   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },
1003   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,
1004     FALSE },
1005   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,
1006     FALSE },
1007   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },
1008   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },
1009   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1010   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
1011 #ifdef ZIPPY
1012   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },
1013   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },
1014   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1015   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
1016   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },
1017   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },
1018   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1019   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
1020   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },
1021   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },
1022   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },
1023   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },
1024   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,
1025     FALSE },
1026   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },
1027   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },
1028   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },
1029   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1030   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
1031   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },
1032   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,
1033     FALSE },
1034   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1035   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1036   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
1037   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },
1038   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },
1039   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },
1040   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },
1041   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1042   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
1043   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },
1044   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },
1045   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1046   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
1047   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },
1048   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },
1049   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },
1050   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */
1051   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },
1052 #endif
1053   { NULL, ArgNone, NULL, FALSE }
1054 };
1055
1056
1057 /* Kludge for indirection files on command line */
1058 char* lastIndirectionFilename;
1059 ArgDescriptor argDescriptorIndirection =
1060 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };
1061
1062
1063 VOID
1064 ExitArgError(char *msg, char *badArg)
1065 {
1066   char buf[MSG_SIZ];
1067
1068   sprintf(buf, "%s %s", msg, badArg);
1069   DisplayFatalError(buf, 0, 2);
1070   exit(2);
1071 }
1072
1073 /* Command line font name parser.  NULL name means do nothing.
1074    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
1075    For backward compatibility, syntax without the colon is also
1076    accepted, but font names with digits in them won't work in that case.
1077 */
1078 VOID
1079 ParseFontName(char *name, MyFontParams *mfp)
1080 {
1081   char *p, *q;
1082   if (name == NULL) return;
1083   p = name;
1084   q = strchr(p, ':');
1085   if (q) {
1086     if (q - p >= sizeof(mfp->faceName))
1087       ExitArgError("Font name too long:", name);
1088     memcpy(mfp->faceName, p, q - p);
1089     mfp->faceName[q - p] = NULLCHAR;
1090     p = q + 1;
1091   } else {
1092     q = mfp->faceName;
1093     while (*p && !isdigit(*p)) {
1094       *q++ = *p++;
1095       if (q - mfp->faceName >= sizeof(mfp->faceName))
1096         ExitArgError("Font name too long:", name);
1097     }
1098     while (q > mfp->faceName && q[-1] == ' ') q--;
1099     *q = NULLCHAR;
1100   }
1101   if (!*p) ExitArgError("Font point size missing:", name);
1102   mfp->pointSize = (float) atof(p);
1103   mfp->bold = (strchr(p, 'b') != NULL);
1104   mfp->italic = (strchr(p, 'i') != NULL);
1105   mfp->underline = (strchr(p, 'u') != NULL);
1106   mfp->strikeout = (strchr(p, 's') != NULL);
1107 }
1108
1109 /* Color name parser.
1110    X version accepts X color names, but this one
1111    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
1112 COLORREF
1113 ParseColorName(char *name)
1114 {
1115   int red, green, blue, count;
1116   char buf[MSG_SIZ];
1117
1118   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
1119   if (count != 3) {
1120     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", 
1121       &red, &green, &blue);
1122   }
1123   if (count != 3) {
1124     sprintf(buf, "Can't parse color name %s", name);
1125     DisplayError(buf, 0);
1126     return RGB(0, 0, 0);
1127   }
1128   return PALETTERGB(red, green, blue);
1129 }
1130
1131
1132 void ParseAttribs(COLORREF *color, int *effects, char* argValue)
1133 {
1134   char *e = argValue;
1135   int eff = 0;
1136
1137   while (*e) {
1138     if (*e == 'b')      eff |= CFE_BOLD;
1139     else if (*e == 'i') eff |= CFE_ITALIC;
1140     else if (*e == 'u') eff |= CFE_UNDERLINE;
1141     else if (*e == 's') eff |= CFE_STRIKEOUT;
1142     else if (*e == '#' || isdigit(*e)) break;
1143     e++;
1144   }
1145   *effects = eff;
1146   *color   = ParseColorName(e);
1147 }
1148
1149
1150 BoardSize
1151 ParseBoardSize(char *name)
1152 {
1153   BoardSize bs = SizeTiny;
1154   while (sizeInfo[bs].name != NULL) {
1155     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;
1156     bs++;
1157   }
1158   ExitArgError("Unrecognized board size value", name);
1159   return bs; /* not reached */
1160 }
1161
1162
1163 char
1164 StringGet(void *getClosure)
1165 {
1166   char **p = (char **) getClosure;
1167   return *((*p)++);
1168 }
1169
1170 char
1171 FileGet(void *getClosure)
1172 {
1173   int c;
1174   FILE* f = (FILE*) getClosure;
1175
1176   c = getc(f);
1177   if (c == EOF)
1178     return NULLCHAR;
1179   else
1180     return (char) c;
1181 }
1182
1183 /* Parse settings file named "name". If file found, return the
1184    full name in fullname and return TRUE; else return FALSE */
1185 BOOLEAN
1186 ParseSettingsFile(char *name, char fullname[MSG_SIZ])
1187 {
1188   char *dummy;
1189   FILE *f;
1190
1191   if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {
1192     f = fopen(fullname, "r");
1193     if (f != NULL) {
1194       ParseArgs(FileGet, f);
1195       fclose(f);
1196       return TRUE;
1197     }
1198   }
1199   return FALSE;
1200 }
1201
1202 VOID
1203 ParseArgs(GetFunc get, void *cl)
1204 {
1205   char argName[ARG_MAX];
1206   char argValue[ARG_MAX];
1207   ArgDescriptor *ad;
1208   char start;
1209   char *q;
1210   int i, octval;
1211   char ch;
1212   int posarg = 0;
1213
1214   ch = get(cl);
1215   for (;;) {
1216     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);
1217     if (ch == NULLCHAR) break;
1218     if (ch == ';') {
1219       /* Comment to end of line */
1220       ch = get(cl);
1221       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);
1222       continue;
1223     } else if (ch == '/' || ch == '-') {
1224       /* Switch */
1225       q = argName;
1226       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&
1227              ch != '\n' && ch != '\t') {
1228         *q++ = ch;
1229         ch = get(cl);
1230       }
1231       *q = NULLCHAR;
1232
1233       for (ad = argDescriptors; ad->argName != NULL; ad++)
1234         if (strcmp(ad->argName, argName + 1) == 0) break;
1235
1236       if (ad->argName == NULL)
1237         ExitArgError("Unrecognized argument", argName);
1238
1239     } else if (ch == '@') {
1240       /* Indirection file */
1241       ad = &argDescriptorIndirection;
1242       ch = get(cl);
1243     } else {
1244       /* Positional argument */
1245       ad = &argDescriptors[posarg++];
1246       strcpy(argName, ad->argName);
1247     }
1248
1249     if (ad->argType == ArgTrue) {
1250       *(Boolean *) ad->argLoc = TRUE;
1251       continue;
1252     }
1253     if (ad->argType == ArgFalse) {
1254       *(Boolean *) ad->argLoc = FALSE;
1255       continue;
1256     }
1257
1258     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);
1259     if (ch == NULLCHAR || ch == '\n') {
1260       ExitArgError("No value provided for argument", argName);
1261     }
1262     q = argValue;
1263     if (ch == '{') {
1264       // Quoting with { }.  No characters have to (or can) be escaped.
1265       // Thus the string cannot contain a '}' character.
1266       start = ch;
1267       ch = get(cl);
1268       while (start) {
1269         switch (ch) {
1270         case NULLCHAR:
1271           start = NULLCHAR;
1272           break;
1273           
1274         case '}':
1275           ch = get(cl);
1276           start = NULLCHAR;
1277           break;
1278
1279         default:
1280           *q++ = ch;
1281           ch = get(cl);
1282           break;
1283         }
1284       }   
1285     } else if (ch == '\'' || ch == '"') {
1286       // Quoting with ' ' or " ", with \ as escape character.
1287       // Inconvenient for long strings that may contain Windows filenames.
1288       start = ch;
1289       ch = get(cl);
1290       while (start) {
1291         switch (ch) {
1292         case NULLCHAR:
1293           start = NULLCHAR;
1294           break;
1295
1296         default:
1297         not_special:
1298           *q++ = ch;
1299           ch = get(cl);
1300           break;
1301
1302         case '\'':
1303         case '\"':
1304           if (ch == start) {
1305             ch = get(cl);
1306             start = NULLCHAR;
1307             break;
1308           } else {
1309             goto not_special;
1310           }
1311
1312         case '\\':
1313           if (ad->argType == ArgFilename
1314               || ad->argType == ArgSettingsFilename) {
1315               goto not_special;
1316           }
1317           ch = get(cl);
1318           switch (ch) {
1319           case NULLCHAR:
1320             ExitArgError("Incomplete \\ escape in value for", argName);
1321             break;
1322           case 'n':
1323             *q++ = '\n';
1324             ch = get(cl);
1325             break;
1326           case 'r':
1327             *q++ = '\r';
1328             ch = get(cl);
1329             break;
1330           case 't':
1331             *q++ = '\t';
1332             ch = get(cl);
1333             break;
1334           case 'b':
1335             *q++ = '\b';
1336             ch = get(cl);
1337             break;
1338           case 'f':
1339             *q++ = '\f';
1340             ch = get(cl);
1341             break;
1342           default:
1343             octval = 0;
1344             for (i = 0; i < 3; i++) {
1345               if (ch >= '0' && ch <= '7') {
1346                 octval = octval*8 + (ch - '0');
1347                 ch = get(cl);
1348               } else {
1349                 break;
1350               }
1351             }
1352             if (i > 0) {
1353               *q++ = (char) octval;
1354             } else {
1355               *q++ = ch;
1356               ch = get(cl);
1357             }
1358             break;
1359           }
1360           break;
1361         }
1362       }
1363     } else {
1364       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {
1365         *q++ = ch;
1366         ch = get(cl);
1367       }
1368     }
1369     *q = NULLCHAR;
1370
1371     switch (ad->argType) {
1372     case ArgInt:
1373       *(int *) ad->argLoc = atoi(argValue);
1374       break;
1375
1376     case ArgFloat:
1377       *(float *) ad->argLoc = (float) atof(argValue);
1378       break;
1379
1380     case ArgString:
1381     case ArgFilename:
1382       *(char **) ad->argLoc = strdup(argValue);
1383       break;
1384
1385     case ArgSettingsFilename:
1386       {
1387         char fullname[MSG_SIZ];
1388         if (ParseSettingsFile(argValue, fullname)) {
1389           if (ad->argLoc != NULL) {
1390             *(char **) ad->argLoc = strdup(fullname);
1391           }
1392         } else {
1393           if (ad->argLoc != NULL) {
1394           } else {
1395             ExitArgError("Failed to open indirection file", argValue);
1396           }
1397         }
1398       }
1399       break;
1400
1401     case ArgBoolean:
1402       switch (argValue[0]) {
1403       case 't':
1404       case 'T':
1405         *(Boolean *) ad->argLoc = TRUE;
1406         break;
1407       case 'f':
1408       case 'F':
1409         *(Boolean *) ad->argLoc = FALSE;
1410         break;
1411       default:
1412         ExitArgError("Unrecognized boolean argument value", argValue);
1413         break;
1414       }
1415       break;
1416
1417     case ArgColor:
1418       *(COLORREF *)ad->argLoc = ParseColorName(argValue);
1419       break;
1420
1421     case ArgAttribs: {
1422       ColorClass cc = (ColorClass)ad->argLoc;
1423       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);
1424       }
1425       break;
1426       
1427     case ArgBoardSize:
1428       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);
1429       break;
1430
1431     case ArgFont:
1432       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);
1433       break;
1434
1435     case ArgCommSettings:
1436       ParseCommSettings(argValue, &dcb);
1437       break;
1438
1439     case ArgNone:
1440       ExitArgError("Unrecognized argument", argValue);
1441       break;
1442     }
1443   }
1444 }
1445
1446 VOID
1447 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
1448 {
1449   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
1450   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
1451   DeleteDC(hdc);
1452   lf->lfWidth = 0;
1453   lf->lfEscapement = 0;
1454   lf->lfOrientation = 0;
1455   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
1456   lf->lfItalic = mfp->italic;
1457   lf->lfUnderline = mfp->underline;
1458   lf->lfStrikeOut = mfp->strikeout;
1459   lf->lfCharSet = DEFAULT_CHARSET;
1460   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
1461   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
1462   lf->lfQuality = DEFAULT_QUALITY;
1463   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
1464   strcpy(lf->lfFaceName, mfp->faceName);
1465 }
1466
1467 VOID
1468 CreateFontInMF(MyFont *mf)
1469 {
1470   LFfromMFP(&mf->lf, &mf->mfp);
1471   if (mf->hf) DeleteObject(mf->hf);
1472   mf->hf = CreateFontIndirect(&mf->lf);
1473 }
1474
1475 VOID
1476 SetDefaultTextAttribs()
1477 {
1478   ColorClass cc;
1479   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1480     ParseAttribs(&textAttribs[cc].color, 
1481                  &textAttribs[cc].effects, 
1482                  defaultTextAttribs[cc]);
1483   }
1484 }
1485
1486 VOID
1487 SetDefaultSounds()
1488 {
1489   ColorClass cc;
1490   SoundClass sc;
1491   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1492     textAttribs[cc].sound.name = strdup("");
1493     textAttribs[cc].sound.data = NULL;
1494   }
1495   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1496     sounds[sc].name = strdup("");
1497     sounds[sc].data = NULL;
1498   }
1499   sounds[(int)SoundBell].name = strdup(SOUND_BELL);
1500 }
1501
1502 VOID
1503 LoadAllSounds()
1504 {
1505   ColorClass cc;
1506   SoundClass sc;
1507   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
1508     MyLoadSound(&textAttribs[cc].sound);
1509   }
1510   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
1511     MyLoadSound(&sounds[sc]);
1512   }
1513 }
1514
1515 VOID
1516 InitAppData(LPSTR lpCmdLine)
1517 {
1518   int i, j;
1519   char buf[ARG_MAX], currDir[MSG_SIZ];
1520   char *dummy, *p;
1521
1522   programName = szAppName;
1523
1524   /* Initialize to defaults */
1525   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);
1526   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);
1527   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);
1528   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);
1529   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);
1530   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);
1531   consoleBackgroundColor = ParseColorName(COLOR_BKGD);
1532   SetDefaultTextAttribs();
1533   SetDefaultSounds();
1534   appData.movesPerSession = MOVES_PER_SESSION;
1535   appData.initString = INIT_STRING;
1536   appData.secondInitString = INIT_STRING;
1537   appData.firstComputerString = COMPUTER_STRING;
1538   appData.secondComputerString = COMPUTER_STRING;
1539   appData.firstChessProgram = FIRST_CHESS_PROGRAM;
1540   appData.secondChessProgram = SECOND_CHESS_PROGRAM;
1541   appData.firstPlaysBlack = FALSE;
1542   appData.noChessProgram = FALSE;
1543   chessProgram = FALSE;
1544   appData.firstHost = FIRST_HOST;
1545   appData.secondHost = SECOND_HOST;
1546   appData.firstDirectory = FIRST_DIRECTORY;
1547   appData.secondDirectory = SECOND_DIRECTORY;
1548   appData.bitmapDirectory = "";
1549   appData.remoteShell = REMOTE_SHELL;
1550   appData.remoteUser = "";
1551   appData.timeDelay = TIME_DELAY;
1552   appData.timeControl = TIME_CONTROL;
1553   appData.timeIncrement = TIME_INCREMENT;
1554   appData.icsActive = FALSE;
1555   appData.icsHost = "";
1556   appData.icsPort = ICS_PORT;
1557   appData.icsCommPort = ICS_COMM_PORT;
1558   appData.icsLogon = ICS_LOGON;
1559   appData.icsHelper = "";
1560   appData.useTelnet = FALSE;
1561   appData.telnetProgram = TELNET_PROGRAM;
1562   appData.gateway = "";
1563   appData.loadGameFile = "";
1564   appData.loadGameIndex = 0;
1565   appData.saveGameFile = "";
1566   appData.autoSaveGames = FALSE;
1567   appData.loadPositionFile = "";
1568   appData.loadPositionIndex = 1;
1569   appData.savePositionFile = "";
1570   appData.matchMode = FALSE;
1571   appData.matchGames = 0;
1572   appData.monoMode = FALSE;
1573   appData.debugMode = FALSE;
1574   appData.clockMode = TRUE;
1575   boardSize = (BoardSize) -1; /* determine by screen size */
1576   appData.Iconic = FALSE; /*unused*/
1577   appData.searchTime = "";
1578   appData.searchDepth = 0;
1579   appData.showCoords = FALSE;
1580   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/
1581   appData.autoCallFlag = FALSE;
1582   appData.flipView = FALSE;
1583   appData.autoFlipView = TRUE;
1584   appData.cmailGameName = "";
1585   appData.alwaysPromoteToQueen = FALSE;
1586   appData.oldSaveStyle = FALSE;
1587   appData.quietPlay = FALSE;
1588   appData.showThinking = FALSE;
1589   appData.ponderNextMove = TRUE;
1590   appData.periodicUpdates = TRUE;
1591   appData.popupExitMessage = TRUE;
1592   appData.popupMoveErrors = FALSE;
1593   appData.autoObserve = FALSE;
1594   appData.autoComment = FALSE;
1595   appData.animate = TRUE;
1596   appData.animSpeed = 10;
1597   appData.animateDragging = TRUE;
1598   appData.highlightLastMove = TRUE;
1599   appData.getMoveList = TRUE;
1600   appData.testLegality = TRUE;
1601   appData.premove = TRUE;
1602   appData.premoveWhite = FALSE;
1603   appData.premoveWhiteText = "";
1604   appData.premoveBlack = FALSE;
1605   appData.premoveBlackText = "";
1606   appData.icsAlarm = TRUE;
1607   appData.icsAlarmTime = 5000;
1608   appData.autoRaiseBoard = TRUE;
1609   appData.localLineEditing = TRUE;
1610   appData.colorize = TRUE;
1611   appData.reuseFirst = TRUE;
1612   appData.reuseSecond = TRUE;
1613   appData.blindfold = FALSE;
1614   dcb.DCBlength = sizeof(DCB);
1615   dcb.BaudRate = 9600;
1616   dcb.fBinary = TRUE;
1617   dcb.fParity = FALSE;
1618   dcb.fOutxCtsFlow = FALSE;
1619   dcb.fOutxDsrFlow = FALSE;
1620   dcb.fDtrControl = DTR_CONTROL_ENABLE;
1621   dcb.fDsrSensitivity = FALSE;
1622   dcb.fTXContinueOnXoff = TRUE;
1623   dcb.fOutX = FALSE;
1624   dcb.fInX = FALSE;
1625   dcb.fNull = FALSE;
1626   dcb.fRtsControl = RTS_CONTROL_ENABLE;
1627   dcb.fAbortOnError = FALSE;
1628   /* Microsoft SDK >= Feb. 2003 (MS VS >= 2002) */
1629   #if (defined(_MSC_VER) && _MSC_VER <= 1200) 
1630         dcb.wReserved = 0;
1631   #else
1632     dcb.wReserved = 0;
1633   #endif
1634   dcb.ByteSize = 7;
1635   dcb.Parity = SPACEPARITY;
1636   dcb.StopBits = ONESTOPBIT;
1637   settingsFileName = SETTINGS_FILE;
1638   saveSettingsOnExit = TRUE;
1639   boardX = CW_USEDEFAULT;
1640   boardY = CW_USEDEFAULT;
1641   consoleX = CW_USEDEFAULT; 
1642   consoleY = CW_USEDEFAULT; 
1643   consoleW = CW_USEDEFAULT;
1644   consoleH = CW_USEDEFAULT;
1645   analysisX = CW_USEDEFAULT; 
1646   analysisY = CW_USEDEFAULT; 
1647   analysisW = CW_USEDEFAULT;
1648   analysisH = CW_USEDEFAULT;
1649   commentX = CW_USEDEFAULT; 
1650   commentY = CW_USEDEFAULT; 
1651   commentW = CW_USEDEFAULT;
1652   commentH = CW_USEDEFAULT;
1653   editTagsX = CW_USEDEFAULT; 
1654   editTagsY = CW_USEDEFAULT; 
1655   editTagsW = CW_USEDEFAULT;
1656   editTagsH = CW_USEDEFAULT;
1657   gameListX = CW_USEDEFAULT; 
1658   gameListY = CW_USEDEFAULT; 
1659   gameListW = CW_USEDEFAULT;
1660   gameListH = CW_USEDEFAULT;
1661   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;
1662   icsNames = ICS_NAMES;
1663   firstChessProgramNames = FCP_NAMES;
1664   secondChessProgramNames = SCP_NAMES;
1665   appData.initialMode = "";
1666   appData.variant = "normal";
1667   appData.firstProtocolVersion = PROTOVER;
1668   appData.secondProtocolVersion = PROTOVER;
1669   appData.showButtonBar = TRUE;
1670 #ifdef ZIPPY
1671   appData.zippyTalk = ZIPPY_TALK;
1672   appData.zippyPlay = ZIPPY_PLAY;
1673   appData.zippyLines = ZIPPY_LINES;
1674   appData.zippyPinhead = ZIPPY_PINHEAD;
1675   appData.zippyPassword = ZIPPY_PASSWORD;
1676   appData.zippyPassword2 = ZIPPY_PASSWORD2;
1677   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;
1678   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;
1679   appData.zippyUseI = ZIPPY_USE_I;
1680   appData.zippyBughouse = ZIPPY_BUGHOUSE;
1681   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;
1682   appData.zippyGameEnd = ZIPPY_GAME_END;
1683   appData.zippyGameStart = ZIPPY_GAME_START;
1684   appData.zippyAdjourn = ZIPPY_ADJOURN;
1685   appData.zippyAbort = ZIPPY_ABORT;
1686   appData.zippyVariants = ZIPPY_VARIANTS;
1687   appData.zippyMaxGames = ZIPPY_MAX_GAMES;
1688   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;
1689 #endif
1690
1691   /* Point font array elements to structures and
1692      parse default font names */
1693   for (i=0; i<NUM_FONTS; i++) {
1694     for (j=0; j<NUM_SIZES; j++) {
1695       font[j][i] = &fontRec[j][i];
1696       ParseFontName(font[j][i]->def, &font[j][i]->mfp);
1697     }
1698   }
1699   
1700   /* Parse default settings file if any */
1701   if (ParseSettingsFile(settingsFileName, buf)) {
1702     settingsFileName = strdup(buf);
1703   }
1704
1705   /* Parse command line */
1706   ParseArgs(StringGet, &lpCmdLine);
1707
1708   /* Propagate options that affect others */
1709   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;
1710   if (appData.icsActive || appData.noChessProgram) {
1711      chessProgram = FALSE;  /* not local chess program mode */
1712   }
1713
1714   /* Open startup dialog if needed */
1715   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||
1716       (appData.icsActive && *appData.icsHost == NULLCHAR) ||
1717       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||
1718                         *appData.secondChessProgram == NULLCHAR))) {
1719     FARPROC lpProc;
1720     
1721     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
1722     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
1723     FreeProcInstance(lpProc);
1724   }
1725
1726   /* Make sure save files land in the right (?) directory */
1727   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {
1728     appData.saveGameFile = strdup(buf);
1729   }
1730   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {
1731     appData.savePositionFile = strdup(buf);
1732   }
1733
1734   /* Finish initialization for fonts and sounds */
1735   for (i=0; i<NUM_FONTS; i++) {
1736     for (j=0; j<NUM_SIZES; j++) {
1737       CreateFontInMF(font[j][i]);
1738     }
1739   }
1740   /* xboard, and older WinBoards, controlled the move sound with the
1741      appData.ringBellAfterMoves option.  In the current WinBoard, we
1742      always turn the option on (so that the backend will call us),
1743      then let the user turn the sound off by setting it to silence if
1744      desired.  To accommodate old winboard.ini files saved by old
1745      versions of WinBoard, we also turn off the sound if the option
1746      was initially set to false. */
1747   if (!appData.ringBellAfterMoves) {
1748     sounds[(int)SoundMove].name = strdup("");
1749     appData.ringBellAfterMoves = TRUE;
1750   }
1751   GetCurrentDirectory(MSG_SIZ, currDir);
1752   SetCurrentDirectory(installDir);
1753   LoadAllSounds();
1754   SetCurrentDirectory(currDir);
1755
1756   p = icsTextMenuString;
1757   if (p[0] == '@') {
1758     FILE* f = fopen(p + 1, "r");
1759     if (f == NULL) {
1760       DisplayFatalError(p + 1, errno, 2);
1761       return;
1762     }
1763     i = fread(buf, 1, sizeof(buf)-1, f);
1764     fclose(f);
1765     buf[i] = NULLCHAR;
1766     p = buf;
1767   }
1768   ParseIcsTextMenu(strdup(p));
1769 }
1770
1771
1772 VOID
1773 InitMenuChecks()
1774 {
1775   HMENU hmenu = GetMenu(hwndMain);
1776
1777   (void) EnableMenuItem(hmenu, IDM_CommPort,
1778                         MF_BYCOMMAND|((appData.icsActive &&
1779                                        *appData.icsCommPort != NULLCHAR) ?
1780                                       MF_ENABLED : MF_GRAYED));
1781   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
1782                        MF_BYCOMMAND|(saveSettingsOnExit ?
1783                                      MF_CHECKED : MF_UNCHECKED));
1784 }
1785
1786
1787 VOID
1788 SaveSettings(char* name)
1789 {
1790   FILE *f;
1791   ArgDescriptor *ad;
1792   WINDOWPLACEMENT wp;
1793   char dir[MSG_SIZ];
1794
1795   if (!hwndMain) return;
1796
1797   GetCurrentDirectory(MSG_SIZ, dir);
1798   SetCurrentDirectory(installDir);
1799   f = fopen(name, "w");
1800   SetCurrentDirectory(dir);
1801   if (f == NULL) {
1802     DisplayError(name, errno);
1803     return;
1804   }
1805   fprintf(f, ";\n");
1806   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);
1807   fprintf(f, ";\n");
1808   fprintf(f, "; You can edit the values of options that are already set in this file,\n");
1809   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");
1810   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");
1811   fprintf(f, ";\n");
1812
1813   wp.length = sizeof(WINDOWPLACEMENT);
1814   GetWindowPlacement(hwndMain, &wp);
1815   boardX = wp.rcNormalPosition.left;
1816   boardY = wp.rcNormalPosition.top;
1817
1818   if (hwndConsole) {
1819     GetWindowPlacement(hwndConsole, &wp);
1820     consoleX = wp.rcNormalPosition.left;
1821     consoleY = wp.rcNormalPosition.top;
1822     consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1823     consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1824   }
1825
1826   if (analysisDialog) {
1827     GetWindowPlacement(analysisDialog, &wp);
1828     analysisX = wp.rcNormalPosition.left;
1829     analysisY = wp.rcNormalPosition.top;
1830     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1831     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1832   }
1833
1834   if (commentDialog) {
1835     GetWindowPlacement(commentDialog, &wp);
1836     commentX = wp.rcNormalPosition.left;
1837     commentY = wp.rcNormalPosition.top;
1838     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1839     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1840   }
1841
1842   if (editTagsDialog) {
1843     GetWindowPlacement(editTagsDialog, &wp);
1844     editTagsX = wp.rcNormalPosition.left;
1845     editTagsY = wp.rcNormalPosition.top;
1846     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1847     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1848   }
1849
1850   if (gameListDialog) {
1851     GetWindowPlacement(gameListDialog, &wp);
1852     gameListX = wp.rcNormalPosition.left;
1853     gameListY = wp.rcNormalPosition.top;
1854     gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
1855     gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
1856   }
1857
1858   for (ad = argDescriptors; ad->argName != NULL; ad++) {
1859     if (!ad->save) continue;
1860     switch (ad->argType) {
1861     case ArgString:
1862       {
1863         char *p = *(char **)ad->argLoc;
1864         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {
1865           /* Quote multiline values or \-containing values
1866              with { } if possible */
1867           fprintf(f, "/%s={%s}\n", ad->argName, p);
1868         } else {
1869           /* Else quote with " " */
1870           fprintf(f, "/%s=\"", ad->argName);
1871           while (*p) {
1872             if (*p == '\n') fprintf(f, "\n");
1873             else if (*p == '\r') fprintf(f, "\\r");
1874             else if (*p == '\t') fprintf(f, "\\t");
1875             else if (*p == '\b') fprintf(f, "\\b");
1876             else if (*p == '\f') fprintf(f, "\\f");
1877             else if (*p < ' ') fprintf(f, "\\%03o", *p);
1878             else if (*p == '\"') fprintf(f, "\\\"");
1879             else if (*p == '\\') fprintf(f, "\\\\");
1880             else putc(*p, f);
1881             p++;
1882           }
1883           fprintf(f, "\"\n");
1884         }
1885       }
1886       break;
1887     case ArgInt:
1888       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);
1889       break;
1890     case ArgFloat:
1891       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);
1892       break;
1893     case ArgBoolean:
1894       fprintf(f, "/%s=%s\n", ad->argName, 
1895         (*(Boolean *)ad->argLoc) ? "true" : "false");
1896       break;
1897     case ArgTrue:
1898       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1899       break;
1900     case ArgFalse:
1901       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
1902       break;
1903     case ArgColor:
1904       {
1905         COLORREF color = *(COLORREF *)ad->argLoc;
1906         fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, 
1907           color&0xff, (color>>8)&0xff, (color>>16)&0xff);
1908       }
1909       break;
1910     case ArgAttribs:
1911       {
1912         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
1913         fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,
1914           (ta->effects & CFE_BOLD) ? "b" : "",
1915           (ta->effects & CFE_ITALIC) ? "i" : "",
1916           (ta->effects & CFE_UNDERLINE) ? "u" : "",
1917           (ta->effects & CFE_STRIKEOUT) ? "s" : "",
1918           (ta->effects) ? " " : "",
1919           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
1920       }
1921       break;
1922     case ArgFilename:
1923       if (strchr(*(char **)ad->argLoc, '\"')) {
1924         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);
1925       } else {
1926         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);
1927       }
1928       break;
1929     case ArgBoardSize:
1930       fprintf(f, "/%s=%s\n", ad->argName,
1931               sizeInfo[*(BoardSize *)ad->argLoc].name);
1932       break;
1933     case ArgFont:
1934       {
1935         int bs;
1936         for (bs=0; bs<NUM_SIZES; bs++) {
1937           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
1938           fprintf(f, "/size=%s ", sizeInfo[bs].name);
1939           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",
1940             ad->argName, mfp->faceName, mfp->pointSize,
1941             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
1942             mfp->bold ? "b" : "",
1943             mfp->italic ? "i" : "",
1944             mfp->underline ? "u" : "",
1945             mfp->strikeout ? "s" : "");
1946         }
1947       }
1948       break;
1949     case ArgCommSettings:
1950       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);
1951     }
1952   }
1953   fclose(f);
1954 }
1955
1956
1957
1958 /*---------------------------------------------------------------------------*\
1959  *
1960  * GDI board drawing routines
1961  *
1962 \*---------------------------------------------------------------------------*/
1963
1964 HBITMAP
1965 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
1966 {
1967   char name[128];
1968
1969   sprintf(name, "%s%d%s", piece, squareSize, suffix);
1970   if (gameInfo.event &&
1971       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
1972       strcmp(name, "k80s") == 0) {
1973     strcpy(name, "tim");
1974   }
1975   return LoadBitmap(hinst, name);
1976 }
1977
1978
1979 /* Insert a color into the program's logical palette
1980    structure.  This code assumes the given color is
1981    the result of the RGB or PALETTERGB macro, and it
1982    knows how those macros work (which is documented).
1983 */
1984 VOID
1985 InsertInPalette(COLORREF color)
1986 {
1987   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
1988
1989   if (pLogPal->palNumEntries++ >= PALETTESIZE) {
1990     DisplayFatalError("Too many colors", 0, 1);
1991     pLogPal->palNumEntries--;
1992     return;
1993   }
1994
1995   pe->peFlags = (char) 0;
1996   pe->peRed = (char) (0xFF & color);
1997   pe->peGreen = (char) (0xFF & (color >> 8));
1998   pe->peBlue = (char) (0xFF & (color >> 16));
1999   return;
2000 }
2001
2002
2003 VOID
2004 InitDrawingColors()
2005 {
2006   if (pLogPal == NULL) {
2007     /* Allocate enough memory for a logical palette with
2008      * PALETTESIZE entries and set the size and version fields
2009      * of the logical palette structure.
2010      */
2011     pLogPal = (NPLOGPALETTE)
2012       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
2013                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));
2014     pLogPal->palVersion    = 0x300;
2015   }
2016   pLogPal->palNumEntries = 0;
2017
2018   InsertInPalette(lightSquareColor);
2019   InsertInPalette(darkSquareColor);
2020   InsertInPalette(whitePieceColor);
2021   InsertInPalette(blackPieceColor);
2022   InsertInPalette(highlightSquareColor);
2023   InsertInPalette(premoveHighlightColor);
2024
2025   /*  create a logical color palette according the information
2026    *  in the LOGPALETTE structure.
2027    */
2028   hPal = CreatePalette((LPLOGPALETTE) pLogPal);
2029
2030   lightSquareBrush = CreateSolidBrush(lightSquareColor);
2031   darkSquareBrush = CreateSolidBrush(darkSquareColor);
2032   whitePieceBrush = CreateSolidBrush(whitePieceColor);
2033   blackPieceBrush = CreateSolidBrush(blackPieceColor);
2034   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
2035 }
2036
2037
2038 int
2039 BoardWidth(int boardSize)
2040 {
2041   return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +
2042           BOARD_SIZE * sizeInfo[boardSize].squareSize;
2043 }
2044
2045 /* Respond to board resize by dragging edge */
2046 VOID
2047 ResizeBoard(int newSizeX, int newSizeY, int flags)
2048 {
2049   BoardSize newSize = NUM_SIZES - 1;
2050   static int recurse = 0;
2051   if (IsIconic(hwndMain)) return;
2052   if (recurse > 0) return;
2053   recurse++;
2054   while (newSize > 0 &&
2055          (newSizeX < sizeInfo[newSize].cliWidth ||
2056           newSizeY < sizeInfo[newSize].cliHeight)) {
2057     newSize--;
2058   } 
2059   boardSize = newSize;
2060   InitDrawingSizes(boardSize, flags);
2061   recurse--;
2062 }
2063
2064
2065
2066 VOID
2067 InitDrawingSizes(BoardSize boardSize, int flags)
2068 {
2069   int i, boardWidth;
2070   ChessSquare piece;
2071   static int oldBoardSize = -1, oldTinyLayout = 0;
2072   HDC hdc;
2073   SIZE clockSize, messageSize;
2074   HFONT oldFont;
2075   char buf[MSG_SIZ];
2076   char *str;
2077   HMENU hmenu = GetMenu(hwndMain);
2078   RECT crect, wrect;
2079   int offby;
2080   LOGBRUSH logbrush;
2081
2082   tinyLayout = sizeInfo[boardSize].tinyLayout;
2083   smallLayout = sizeInfo[boardSize].smallLayout;
2084   squareSize = sizeInfo[boardSize].squareSize;
2085   lineGap = sizeInfo[boardSize].lineGap;
2086
2087   if (tinyLayout != oldTinyLayout) {
2088     long style = GetWindowLong(hwndMain, GWL_STYLE);
2089     if (tinyLayout) {
2090       style &= ~WS_SYSMENU;
2091       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
2092                  "&Minimize\tCtrl+F4");
2093     } else {
2094       style |= WS_SYSMENU;
2095       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
2096     }
2097     SetWindowLong(hwndMain, GWL_STYLE, style);
2098
2099     for (i=0; menuBarText[tinyLayout][i]; i++) {
2100       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, 
2101         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
2102     }
2103     DrawMenuBar(hwndMain);
2104   }
2105
2106   boardWidth = BoardWidth(boardSize);
2107
2108   /* Get text area sizes */
2109   hdc = GetDC(hwndMain);
2110   if (appData.clockMode) {
2111     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
2112   } else {
2113     sprintf(buf, "White");
2114   }
2115   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
2116   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
2117   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2118   str = "We only care about the height here";
2119   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
2120   SelectObject(hdc, oldFont);
2121   ReleaseDC(hwndMain, hdc);
2122
2123   /* Compute where everything goes */
2124   whiteRect.left = OUTER_MARGIN;
2125   whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
2126   whiteRect.top = OUTER_MARGIN;
2127   whiteRect.bottom = whiteRect.top + clockSize.cy;
2128
2129   blackRect.left = whiteRect.right + INNER_MARGIN;
2130   blackRect.right = blackRect.left + boardWidth/2 - 1;
2131   blackRect.top = whiteRect.top;
2132   blackRect.bottom = whiteRect.bottom;
2133
2134   messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;
2135   if (appData.showButtonBar) {
2136     messageRect.right = blackRect.right
2137       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
2138   } else {
2139     messageRect.right = blackRect.right;
2140   }
2141   messageRect.top = whiteRect.bottom + INNER_MARGIN;
2142   messageRect.bottom = messageRect.top + messageSize.cy;
2143
2144   boardRect.left = whiteRect.left;
2145   boardRect.right = boardRect.left + boardWidth;
2146   boardRect.top = messageRect.bottom + INNER_MARGIN;
2147   boardRect.bottom = boardRect.top + boardWidth;
2148
2149   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
2150   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
2151   winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
2152   winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
2153     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
2154   GetWindowRect(hwndMain, &wrect);
2155   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2156                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2157   /* compensate if menu bar wrapped */
2158   GetClientRect(hwndMain, &crect);
2159   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
2160   winHeight += offby;
2161   switch (flags) {
2162   case WMSZ_TOPLEFT:
2163     SetWindowPos(hwndMain, NULL, 
2164                  wrect.right - winWidth, wrect.bottom - winHeight, 
2165                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2166     break;
2167
2168   case WMSZ_TOPRIGHT:
2169   case WMSZ_TOP:
2170     SetWindowPos(hwndMain, NULL, 
2171                  wrect.left, wrect.bottom - winHeight, 
2172                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2173     break;
2174
2175   case WMSZ_BOTTOMLEFT:
2176   case WMSZ_LEFT:
2177     SetWindowPos(hwndMain, NULL, 
2178                  wrect.right - winWidth, wrect.top, 
2179                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
2180     break;
2181
2182   case WMSZ_BOTTOMRIGHT:
2183   case WMSZ_BOTTOM:
2184   case WMSZ_RIGHT:
2185   default:
2186     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
2187                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
2188     break;
2189   }
2190
2191   hwndPause = NULL;
2192   for (i = 0; i < N_BUTTONS; i++) {
2193     if (buttonDesc[i].hwnd != NULL) {
2194       DestroyWindow(buttonDesc[i].hwnd);
2195       buttonDesc[i].hwnd = NULL;
2196     }
2197     if (appData.showButtonBar) {
2198       buttonDesc[i].hwnd =
2199         CreateWindow("BUTTON", buttonDesc[i].label,
2200                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
2201                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
2202                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
2203                      (HMENU) buttonDesc[i].id,
2204                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
2205       if (tinyLayout) {
2206         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, 
2207                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
2208                     MAKELPARAM(FALSE, 0));
2209       }
2210       if (buttonDesc[i].id == IDM_Pause)
2211         hwndPause = buttonDesc[i].hwnd;
2212       buttonDesc[i].wndproc = (WNDPROC)
2213         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
2214     }
2215   }
2216   if (gridPen != NULL) DeleteObject(gridPen);
2217   if (highlightPen != NULL) DeleteObject(highlightPen);
2218   if (premovePen != NULL) DeleteObject(premovePen);
2219   if (lineGap != 0) {
2220     logbrush.lbStyle = BS_SOLID;
2221     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
2222     gridPen =
2223       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2224                    lineGap, &logbrush, 0, NULL);
2225     logbrush.lbColor = highlightSquareColor;
2226     highlightPen =
2227       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2228                    lineGap, &logbrush, 0, NULL);
2229
2230     logbrush.lbColor = premoveHighlightColor; 
2231     premovePen =
2232       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
2233                    lineGap, &logbrush, 0, NULL);
2234
2235     for (i = 0; i < BOARD_SIZE + 1; i++) {
2236       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
2237       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;
2238       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
2239         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
2240       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
2241         BOARD_SIZE * (squareSize + lineGap);
2242       gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =
2243         gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +
2244         lineGap / 2 + (i * (squareSize + lineGap));
2245       gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =
2246         boardRect.top + BOARD_SIZE * (squareSize + lineGap);
2247       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
2248     }
2249   }
2250
2251   if (boardSize == oldBoardSize) return;
2252   oldBoardSize = boardSize;
2253   oldTinyLayout = tinyLayout;
2254
2255   /* Load piece bitmaps for this board size */
2256   for (i=0; i<=2; i++) {
2257     for (piece = WhitePawn;
2258          (int) piece <= (int) WhiteKing;
2259          piece = (ChessSquare) ((int) piece + 1)) {
2260       if (pieceBitmap[i][piece] != NULL)
2261         DeleteObject(pieceBitmap[i][piece]);
2262     }
2263   }
2264
2265   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
2266   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
2267   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
2268   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
2269   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
2270   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
2271   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
2272   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
2273   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
2274   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
2275   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
2276   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
2277   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
2278   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
2279   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
2280   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
2281   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
2282   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
2283
2284 }
2285
2286 HBITMAP
2287 PieceBitmap(ChessSquare p, int kind)
2288 {
2289   if ((int) p >= (int) BlackPawn)
2290     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
2291
2292   return pieceBitmap[kind][(int) p];
2293 }
2294
2295 /***************************************************************/
2296
2297 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
2298 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
2299 /*
2300 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
2301 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
2302 */
2303
2304 VOID
2305 SquareToPos(int row, int column, int * x, int * y)
2306 {
2307   if (flipView) {
2308     *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
2309     *y = boardRect.top + lineGap + row * (squareSize + lineGap);
2310   } else {
2311     *x = boardRect.left + lineGap + column * (squareSize + lineGap);
2312     *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
2313   }
2314 }
2315
2316 VOID
2317 DrawCoordsOnDC(HDC hdc)
2318 {
2319   static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};
2320   static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};
2321   char str[2] = { NULLCHAR, NULLCHAR };
2322   int oldMode, oldAlign, x, y, start, i;
2323   HFONT oldFont;
2324   HBRUSH oldBrush;
2325
2326   if (!appData.showCoords)
2327     return;
2328
2329   start = flipView ? 0 : 8;
2330
2331   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
2332   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
2333   oldAlign = GetTextAlign(hdc);
2334   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
2335
2336   y = boardRect.top + lineGap;
2337   x = boardRect.left + lineGap;
2338
2339   SetTextAlign(hdc, TA_LEFT|TA_TOP);
2340   for (i = 0; i < 8; i++) {
2341     str[0] = files[start + i];
2342     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
2343     y += squareSize + lineGap;
2344   }
2345
2346   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
2347   for (i = 0; i < 8; i++) {
2348     str[0] = ranks[start + i];
2349     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
2350     x += squareSize + lineGap;
2351   }    
2352
2353   SelectObject(hdc, oldBrush);
2354   SetBkMode(hdc, oldMode);
2355   SetTextAlign(hdc, oldAlign);
2356   SelectObject(hdc, oldFont);
2357 }
2358
2359 VOID
2360 DrawGridOnDC(HDC hdc)
2361 {
2362   HPEN oldPen;
2363  
2364   if (lineGap != 0) {
2365     oldPen = SelectObject(hdc, gridPen);
2366     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);
2367     SelectObject(hdc, oldPen);
2368   }
2369 }
2370
2371 #define HIGHLIGHT_PEN 0
2372 #define PREMOVE_PEN   1
2373
2374 VOID
2375 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
2376 {
2377   int x1, y1;
2378   HPEN oldPen, hPen;
2379   if (lineGap == 0) return;
2380   if (flipView) {
2381     x1 = boardRect.left +
2382       lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);
2383     y1 = boardRect.top +
2384       lineGap/2 + y * (squareSize + lineGap);
2385   } else {
2386     x1 = boardRect.left +
2387       lineGap/2 + x * (squareSize + lineGap);
2388     y1 = boardRect.top +
2389       lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);
2390   }
2391   hPen = pen ? premovePen : highlightPen;
2392   oldPen = SelectObject(hdc, on ? hPen : gridPen);
2393   MoveToEx(hdc, x1, y1, NULL);
2394   LineTo(hdc, x1 + squareSize + lineGap, y1);
2395   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
2396   LineTo(hdc, x1, y1 + squareSize + lineGap);
2397   LineTo(hdc, x1, y1);
2398   SelectObject(hdc, oldPen);
2399 }
2400
2401 VOID
2402 DrawHighlightsOnDC(HDC hdc)
2403 {
2404   int i;
2405   for (i=0; i<2; i++) {
2406     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) 
2407       DrawHighlightOnDC(hdc, TRUE,
2408                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,
2409                         HIGHLIGHT_PEN);
2410   }
2411   for (i=0; i<2; i++) {
2412     if (premoveHighlightInfo.sq[i].x >= 0 && 
2413         premoveHighlightInfo.sq[i].y >= 0) {
2414         DrawHighlightOnDC(hdc, TRUE,
2415                           premoveHighlightInfo.sq[i].x, 
2416                           premoveHighlightInfo.sq[i].y,
2417                           PREMOVE_PEN);
2418     }
2419   }
2420 }
2421
2422 /* Note: sqcolor is used only in monoMode */
2423 /* Note that this code is largely duplicated in woptions.c,
2424    function DrawSampleSquare, so that needs to be updated too */
2425 VOID
2426 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
2427 {
2428   HBITMAP oldBitmap;
2429   HBRUSH oldBrush;
2430
2431   if (appData.blindfold) return;
2432
2433   if (appData.monoMode) {
2434     SelectObject(tmphdc, PieceBitmap(piece, 
2435       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
2436     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
2437            sqcolor ? SRCCOPY : NOTSRCCOPY);
2438   } else {
2439     if (color) {
2440       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
2441       oldBrush = SelectObject(hdc, whitePieceBrush);
2442       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2443 #if 0
2444       /* Use black piece color for outline of white pieces */
2445       /* Not sure this looks really good (though xboard does it).
2446          Maybe better to have another selectable color, default black */
2447       SelectObject(hdc, blackPieceBrush); /* could have own brush */
2448       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2449       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2450 #else
2451       /* Use black for outline of white pieces */
2452       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
2453       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);
2454 #endif
2455     } else {
2456 #if 0
2457       /* Use white piece color for details of black pieces */
2458       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the
2459          WHITE_PIECE ones aren't always the right shape. */
2460       /* Not sure this looks really good (though xboard does it).
2461          Maybe better to have another selectable color, default medium gray? */
2462       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));
2463       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */
2464       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2465       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2466       SelectObject(hdc, blackPieceBrush);
2467       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2468 #else
2469       /* Use square color for details of black pieces */
2470       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
2471       oldBrush = SelectObject(hdc, blackPieceBrush);
2472       BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
2473 #endif
2474     }
2475     SelectObject(hdc, oldBrush);
2476     SelectObject(tmphdc, oldBitmap);
2477   }
2478 }
2479
2480 VOID
2481 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
2482 {
2483   int row, column, x, y, square_color, piece_color;
2484   ChessSquare piece;
2485   HBRUSH oldBrush;
2486
2487   for (row = 0; row < BOARD_SIZE; row++) {
2488     for (column = 0; column < BOARD_SIZE; column++) {
2489   
2490       SquareToPos(row, column, &x, &y);
2491
2492       piece = board[row][column];
2493
2494       square_color = ((column + row) % 2) == 1;
2495       piece_color = (int) piece < (int) BlackPawn;
2496
2497       if (appData.monoMode) {
2498         if (piece == EmptySquare) {
2499           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
2500                  square_color ? WHITENESS : BLACKNESS);
2501         } else {
2502           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
2503         }
2504       } else {
2505         oldBrush = SelectObject(hdc, square_color ?
2506                                 lightSquareBrush : darkSquareBrush);
2507         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
2508         SelectObject(hdc, oldBrush);
2509         if (piece != EmptySquare)
2510           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
2511       }
2512     }
2513   }
2514 }
2515
2516 #define MAX_CLIPS 200   /* more than enough */
2517
2518 VOID
2519 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
2520 {
2521   static Board lastReq, lastDrawn;
2522   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
2523   static int lastDrawnFlipView = 0;
2524   static int lastReqValid = 0, lastDrawnValid = 0;
2525   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
2526   HDC tmphdc;
2527   HDC hdcmem;
2528   HBITMAP bufferBitmap;
2529   HBITMAP oldBitmap;
2530   RECT Rect;
2531   HRGN clips[MAX_CLIPS];
2532   ChessSquare dragged_piece = EmptySquare;
2533
2534   /* I'm undecided on this - this function figures out whether a full
2535    * repaint is necessary on its own, so there's no real reason to have the
2536    * caller tell it that.  I think this can safely be set to FALSE - but
2537    * if we trust the callers not to request full repaints unnessesarily, then
2538    * we could skip some clipping work.  In other words, only request a full
2539    * redraw when the majority of pieces have changed positions (ie. flip, 
2540    * gamestart and similar)  --Hawk
2541    */
2542   Boolean fullrepaint = repaint;
2543
2544   if (board == NULL) {
2545     if (!lastReqValid) {
2546       return;
2547     }
2548     board = lastReq;
2549   } else {
2550     CopyBoard(lastReq, board);
2551     lastReqValid = 1;
2552   }
2553
2554   if (doingSizing) {
2555     return;
2556   }
2557
2558   if (IsIconic(hwndMain)) {
2559     return;
2560   }
2561
2562   if (hdc == NULL) {
2563     hdc = GetDC(hwndMain);
2564     if (!appData.monoMode) {
2565       SelectPalette(hdc, hPal, FALSE);
2566       RealizePalette(hdc);
2567     }
2568     releaseDC = TRUE;
2569   } else {
2570     releaseDC = FALSE;
2571   }
2572
2573 #if 0
2574   fprintf(debugFP, "*******************************\n"
2575                    "repaint = %s\n"
2576                    "dragInfo.from (%d,%d)\n"
2577                    "dragInfo.start (%d,%d)\n"
2578                    "dragInfo.pos (%d,%d)\n"
2579                    "dragInfo.lastpos (%d,%d)\n", 
2580                     repaint ? "TRUE" : "FALSE",
2581                     dragInfo.from.x, dragInfo.from.y, 
2582                     dragInfo.start.x, dragInfo.start.y,
2583                     dragInfo.pos.x, dragInfo.pos.y,
2584                     dragInfo.lastpos.x, dragInfo.lastpos.y);
2585   fprintf(debugFP, "prev:  ");
2586   for (row = 0; row < 8; row++) {
2587     for (column = 0; column < 8; column++) {
2588       fprintf(debugFP, "%d ", lastDrawn[row][column]);
2589     }
2590   }
2591   fprintf(debugFP, "\n");
2592   fprintf(debugFP, "board: ");
2593   for (row = 0; row < 8; row++) {
2594     for (column = 0; column < 8; column++) {
2595       fprintf(debugFP, "%d ", board[row][column]);
2596     }
2597   }
2598   fprintf(debugFP, "\n");
2599   fflush(debugFP);
2600 #endif
2601
2602   /* Create some work-DCs */
2603   hdcmem = CreateCompatibleDC(hdc);
2604   tmphdc = CreateCompatibleDC(hdc);
2605
2606   /* Figure out which squares need updating by comparing the 
2607    * newest board with the last drawn board and checking if
2608    * flipping has changed.
2609    */
2610   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
2611     for (row = 0; row < 8; row++) {
2612       for (column = 0; column < 8; column++) {
2613         if (lastDrawn[row][column] != board[row][column]) {
2614           SquareToPos(row, column, &x, &y);
2615           clips[num_clips++] =
2616             CreateRectRgn(x, y, x + squareSize, y + squareSize);
2617         }
2618       }
2619     }
2620     for (i=0; i<2; i++) {
2621       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
2622           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
2623         if (lastDrawnHighlight.sq[i].x >= 0 &&
2624             lastDrawnHighlight.sq[i].y >= 0) {
2625           SquareToPos(lastDrawnHighlight.sq[i].y,
2626                       lastDrawnHighlight.sq[i].x, &x, &y);
2627           clips[num_clips++] =
2628             CreateRectRgn(x - lineGap, y - lineGap, 
2629                           x + squareSize + lineGap, y + squareSize + lineGap);
2630         }
2631         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
2632           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
2633           clips[num_clips++] =
2634             CreateRectRgn(x - lineGap, y - lineGap, 
2635                           x + squareSize + lineGap, y + squareSize + lineGap);
2636         }
2637       }
2638     }
2639     for (i=0; i<2; i++) {
2640       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
2641           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
2642         if (lastDrawnPremove.sq[i].x >= 0 &&
2643             lastDrawnPremove.sq[i].y >= 0) {
2644           SquareToPos(lastDrawnPremove.sq[i].y,
2645                       lastDrawnPremove.sq[i].x, &x, &y);
2646           clips[num_clips++] =
2647             CreateRectRgn(x - lineGap, y - lineGap, 
2648                           x + squareSize + lineGap, y + squareSize + lineGap);
2649         }
2650         if (premoveHighlightInfo.sq[i].x >= 0 && 
2651             premoveHighlightInfo.sq[i].y >= 0) {
2652           SquareToPos(premoveHighlightInfo.sq[i].y, 
2653                       premoveHighlightInfo.sq[i].x, &x, &y);
2654           clips[num_clips++] =
2655             CreateRectRgn(x - lineGap, y - lineGap, 
2656                           x + squareSize + lineGap, y + squareSize + lineGap);
2657         }
2658       }
2659     }
2660   } else {
2661     fullrepaint = TRUE;
2662   }
2663
2664   /* Create a buffer bitmap - this is the actual bitmap
2665    * being written to.  When all the work is done, we can
2666    * copy it to the real DC (the screen).  This avoids
2667    * the problems with flickering.
2668    */
2669   GetClientRect(hwndMain, &Rect);
2670   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
2671                                         Rect.bottom-Rect.top+1);
2672   oldBitmap = SelectObject(hdcmem, bufferBitmap);
2673   if (!appData.monoMode) {
2674     SelectPalette(hdcmem, hPal, FALSE);
2675   }
2676
2677   /* Create clips for dragging */
2678   if (!fullrepaint) {
2679     if (dragInfo.from.x >= 0) {
2680       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
2681       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2682     }
2683     if (dragInfo.start.x >= 0) {
2684       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
2685       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2686     }
2687     if (dragInfo.pos.x >= 0) {
2688       x = dragInfo.pos.x - squareSize / 2;
2689       y = dragInfo.pos.y - squareSize / 2;
2690       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2691     }
2692     if (dragInfo.lastpos.x >= 0) {
2693       x = dragInfo.lastpos.x - squareSize / 2;
2694       y = dragInfo.lastpos.y - squareSize / 2;
2695       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
2696     }
2697   }
2698
2699   /* If dragging is in progress, we temporarely remove the piece */
2700   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
2701     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
2702     board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
2703   }
2704
2705   /* Are we animating a move?  
2706    * If so, 
2707    *   - remove the piece from the board (temporarely)
2708    *   - calculate the clipping region
2709    */
2710   if (!fullrepaint) {
2711     if (animInfo.piece != EmptySquare) {
2712       board[animInfo.from.y][animInfo.from.x] = EmptySquare;
2713       x = boardRect.left + animInfo.lastpos.x;
2714       y = boardRect.top + animInfo.lastpos.y;
2715       x2 = boardRect.left + animInfo.pos.x;
2716       y2 = boardRect.top + animInfo.pos.y;
2717       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
2718       /* Slight kludge.  The real problem is that after AnimateMove is
2719          done, the position on the screen does not match lastDrawn.
2720          This currently causes trouble only on e.p. captures in
2721          atomic, where the piece moves to an empty square and then
2722          explodes.  The old and new positions both had an empty square
2723          at the destination, but animation has drawn a piece there and
2724          we have to remember to erase it. */
2725       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
2726     }
2727   }
2728
2729   /* No clips?  Make sure we have fullrepaint set to TRUE */
2730   if (num_clips == 0)
2731     fullrepaint = TRUE;
2732
2733   /* Set clipping on the memory DC */
2734   if (!fullrepaint) {
2735     SelectClipRgn(hdcmem, clips[0]);
2736     for (x = 1; x < num_clips; x++) {
2737       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
2738         abort();  // this should never ever happen!
2739     }
2740   }
2741
2742   /* Do all the drawing to the memory DC */
2743   DrawGridOnDC(hdcmem);
2744   DrawHighlightsOnDC(hdcmem);
2745   DrawBoardOnDC(hdcmem, board, tmphdc);
2746   DrawCoordsOnDC(hdcmem);
2747
2748   /* Put the dragged piece back into place and draw it */
2749   if (dragged_piece != EmptySquare) {
2750     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
2751     x = dragInfo.pos.x - squareSize / 2;
2752     y = dragInfo.pos.y - squareSize / 2;
2753     DrawPieceOnDC(hdcmem, dragged_piece,
2754                   ((int) dragged_piece < (int) BlackPawn), 
2755                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
2756   }   
2757   
2758   /* Put the animated piece back into place and draw it */
2759   if (animInfo.piece != EmptySquare) {
2760     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;
2761     x = boardRect.left + animInfo.pos.x;
2762     y = boardRect.top + animInfo.pos.y;
2763     DrawPieceOnDC(hdcmem, animInfo.piece,
2764                   ((int) animInfo.piece < (int) BlackPawn),
2765                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
2766   }
2767
2768   /* Release the bufferBitmap by selecting in the old bitmap 
2769    * and delete the memory DC
2770    */
2771   SelectObject(hdcmem, oldBitmap);
2772   DeleteDC(hdcmem);
2773
2774   /* Set clipping on the target DC */
2775   if (!fullrepaint) {
2776     SelectClipRgn(hdc, clips[0]);
2777     for (x = 1; x < num_clips; x++) {
2778       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
2779         abort();   // this should never ever happen!
2780     } 
2781   }
2782
2783   /* Copy the new bitmap onto the screen in one go.
2784    * This way we avoid any flickering
2785    */
2786   oldBitmap = SelectObject(tmphdc, bufferBitmap);
2787   BitBlt(hdc, boardRect.left, boardRect.top,
2788          boardRect.right - boardRect.left,
2789          boardRect.bottom - boardRect.top,
2790          tmphdc, boardRect.left, boardRect.top, SRCCOPY);
2791   SelectObject(tmphdc, oldBitmap);
2792
2793   /* Massive cleanup */
2794   for (x = 0; x < num_clips; x++)
2795     DeleteObject(clips[x]);
2796
2797   DeleteDC(tmphdc);
2798   DeleteObject(bufferBitmap);
2799
2800   if (releaseDC) 
2801     ReleaseDC(hwndMain, hdc);
2802   
2803   if (lastDrawnFlipView != flipView) {
2804     if (flipView)
2805       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
2806     else
2807       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
2808   }
2809
2810   CopyBoard(lastDrawn, board);
2811   lastDrawnHighlight = highlightInfo;
2812   lastDrawnPremove   = premoveHighlightInfo;
2813   lastDrawnFlipView = flipView;
2814   lastDrawnValid = 1;
2815 }
2816
2817
2818 /*---------------------------------------------------------------------------*\
2819 | CLIENT PAINT PROCEDURE
2820 |   This is the main event-handler for the WM_PAINT message.
2821 |
2822 \*---------------------------------------------------------------------------*/
2823 VOID
2824 PaintProc(HWND hwnd)
2825 {
2826   HDC         hdc;
2827   PAINTSTRUCT ps;
2828   HFONT       oldFont;
2829
2830   if(hdc = BeginPaint(hwnd, &ps)) {
2831     if (IsIconic(hwnd)) {
2832       DrawIcon(hdc, 2, 2, iconCurrent);
2833     } else {
2834       if (!appData.monoMode) {
2835         SelectPalette(hdc, hPal, FALSE);
2836         RealizePalette(hdc);
2837       }
2838       HDCDrawPosition(hdc, 1, NULL);
2839       oldFont =
2840         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
2841       ExtTextOut(hdc, messageRect.left, messageRect.top,
2842                  ETO_CLIPPED|ETO_OPAQUE,
2843                  &messageRect, messageText, strlen(messageText), NULL);
2844       SelectObject(hdc, oldFont);
2845       DisplayBothClocks();
2846     }
2847     EndPaint(hwnd,&ps);
2848   }
2849
2850   return;
2851 }
2852
2853
2854 /*
2855  * If the user selects on a border boundary, return -1; if off the board,
2856  *   return -2.  Otherwise map the event coordinate to the square.
2857  * The offset boardRect.left or boardRect.top must already have been
2858  *   subtracted from x.
2859  */
2860 int
2861 EventToSquare(int x)
2862 {
2863   if (x <= 0)
2864     return -2;
2865   if (x < lineGap)
2866     return -1;
2867   x -= lineGap;
2868   if ((x % (squareSize + lineGap)) >= squareSize)
2869     return -1;
2870   x /= (squareSize + lineGap);
2871   if (x >= BOARD_SIZE)
2872     return -2;
2873   return x;
2874 }
2875
2876 typedef struct {
2877   char piece;
2878   int command;
2879   char* name;
2880 } DropEnable;
2881
2882 DropEnable dropEnables[] = {
2883   { 'P', DP_Pawn, "Pawn" },
2884   { 'N', DP_Knight, "Knight" },
2885   { 'B', DP_Bishop, "Bishop" },
2886   { 'R', DP_Rook, "Rook" },
2887   { 'Q', DP_Queen, "Queen" },
2888 };
2889
2890 VOID
2891 SetupDropMenu(HMENU hmenu)
2892 {
2893   int i, count, enable;
2894   char *p;
2895   extern char white_holding[], black_holding[];
2896   char item[MSG_SIZ];
2897
2898   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
2899     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2900                dropEnables[i].piece);
2901     count = 0;
2902     while (p && *p++ == dropEnables[i].piece) count++;
2903     sprintf(item, "%s  %d", dropEnables[i].name, count);
2904     enable = count > 0 || !appData.testLegality
2905       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2906                       && !appData.icsActive);
2907     ModifyMenu(hmenu, dropEnables[i].command,
2908                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
2909                dropEnables[i].command, item);
2910   }
2911 }
2912
2913 static int fromX = -1, fromY = -1, toX, toY;
2914
2915 /* Event handler for mouse messages */
2916 VOID
2917 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2918 {
2919   int x, y;
2920   POINT pt;
2921   static int recursive = 0;
2922   HMENU hmenu;
2923   BOOLEAN saveAnimate;
2924   static BOOLEAN sameAgain = FALSE;
2925
2926   if (recursive) {
2927     if (message == WM_MBUTTONUP) {
2928       /* Hideous kludge to fool TrackPopupMenu into paying attention
2929          to the middle button: we simulate pressing the left button too!
2930          */
2931       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
2932       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
2933     }
2934     return;
2935   }
2936   recursive++;
2937   
2938   pt.x = LOWORD(lParam);
2939   pt.y = HIWORD(lParam);
2940   x = EventToSquare(pt.x - boardRect.left);
2941   y = EventToSquare(pt.y - boardRect.top);
2942   if (!flipView && y >= 0) {
2943     y = BOARD_SIZE - 1 - y;
2944   }
2945   if (flipView && x >= 0) {
2946     x = BOARD_SIZE - 1 - x;
2947   }
2948
2949   switch (message) {
2950   case WM_LBUTTONDOWN:
2951     ErrorPopDown();
2952     sameAgain = FALSE;
2953     if (y == -2) {
2954       /* Downclick vertically off board; check if on clock */
2955       if (PtInRect((LPRECT) &whiteRect, pt)) {
2956         if (gameMode == EditPosition) {
2957           SetWhiteToPlayEvent();
2958         } else if (gameMode == IcsPlayingBlack ||
2959                    gameMode == MachinePlaysWhite) {
2960           CallFlagEvent();
2961         }
2962       } else if (PtInRect((LPRECT) &blackRect, pt)) {
2963         if (gameMode == EditPosition) {
2964           SetBlackToPlayEvent();
2965         } else if (gameMode == IcsPlayingWhite ||
2966                    gameMode == MachinePlaysBlack) {
2967           CallFlagEvent();
2968         }
2969       }
2970       if (!appData.highlightLastMove) {
2971         ClearHighlights();
2972         DrawPosition(FALSE, NULL);
2973       }
2974       fromX = fromY = -1;
2975       dragInfo.start.x = dragInfo.start.y = -1;
2976       dragInfo.from = dragInfo.start;
2977       break;
2978     } else if (x < 0 || y < 0) {
2979       break;
2980     } else if (fromX == x && fromY == y) {
2981       /* Downclick on same square again */
2982       ClearHighlights();
2983       DrawPosition(FALSE, NULL);
2984       sameAgain = TRUE;  
2985     } else if (fromX != -1) {
2986       /* Downclick on different square */
2987       ChessSquare pdown, pup;
2988       pdown = boards[currentMove][fromY][fromX];
2989       pup = boards[currentMove][y][x];
2990       if (gameMode == EditPosition ||
2991           !((WhitePawn <= pdown && pdown <= WhiteKing &&
2992              WhitePawn <= pup && pup <= WhiteKing) ||
2993             (BlackPawn <= pdown && pdown <= BlackKing &&
2994              BlackPawn <= pup && pup <= BlackKing))) {
2995         /* EditPosition, empty square, or different color piece;
2996            click-click move is possible */
2997         toX = x;
2998         toY = y;
2999         if (IsPromotion(fromX, fromY, toX, toY)) {
3000           if (appData.alwaysPromoteToQueen) {
3001             UserMoveEvent(fromX, fromY, toX, toY, 'q');
3002             if (!appData.highlightLastMove) {
3003               ClearHighlights();
3004               DrawPosition(FALSE, NULL);
3005             }
3006           } else {
3007             SetHighlights(fromX, fromY, toX, toY);
3008             DrawPosition(FALSE, NULL);
3009             PromotionPopup(hwnd);
3010           }
3011         } else {        /* not a promotion */
3012           if (appData.animate || appData.highlightLastMove) {
3013             SetHighlights(fromX, fromY, toX, toY);
3014           } else {
3015             ClearHighlights();
3016           }
3017           UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3018           if (appData.animate && !appData.highlightLastMove) {
3019             ClearHighlights();
3020             DrawPosition(FALSE, NULL);
3021           }
3022         }
3023         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3024         fromX = fromY = -1;
3025         break;
3026       }
3027       ClearHighlights();
3028       DrawPosition(FALSE, NULL);
3029     }
3030     /* First downclick, or restart on a square with same color piece */
3031     if (!frozen && OKToStartUserMove(x, y)) {
3032       fromX = x;
3033       fromY = y;
3034       dragInfo.lastpos = pt;
3035       dragInfo.from.x = fromX;
3036       dragInfo.from.y = fromY;
3037       dragInfo.start = dragInfo.from;
3038       SetCapture(hwndMain);
3039     } else {
3040       fromX = fromY = -1;
3041       dragInfo.start.x = dragInfo.start.y = -1;
3042       dragInfo.from = dragInfo.start;
3043     }
3044     break;
3045
3046   case WM_LBUTTONUP:
3047     ReleaseCapture();
3048     if (fromX == -1) break;
3049     if (x == fromX && y == fromY) {
3050       dragInfo.from.x = dragInfo.from.y = -1;
3051       /* Upclick on same square */
3052       if (sameAgain) {
3053         /* Clicked same square twice: abort click-click move */
3054         fromX = fromY = -1;
3055         gotPremove = 0;
3056         ClearPremoveHighlights();
3057       } else {
3058         /* First square clicked: start click-click move */
3059         SetHighlights(fromX, fromY, -1, -1);
3060       }
3061       DrawPosition(FALSE, NULL);
3062     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {
3063       /* Errant click; ignore */
3064       break;
3065     } else {
3066       /* Finish drag move */
3067       dragInfo.from.x = dragInfo.from.y = -1;
3068       toX = x;
3069       toY = y;
3070       saveAnimate = appData.animate; /* sorry, Hawk :) */
3071       appData.animate = appData.animate && !appData.animateDragging;
3072       if (IsPromotion(fromX, fromY, toX, toY)) {
3073         if (appData.alwaysPromoteToQueen) {
3074           UserMoveEvent(fromX, fromY, toX, toY, 'q');
3075         } else {
3076           DrawPosition(FALSE, NULL);
3077           PromotionPopup(hwnd);
3078         }
3079       } else {
3080         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
3081       }
3082       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3083       appData.animate = saveAnimate;
3084       fromX = fromY = -1;
3085       if (appData.highlightDragging && !appData.highlightLastMove) {
3086         ClearHighlights();
3087       }
3088       if (appData.animate || appData.animateDragging ||
3089           appData.highlightDragging || gotPremove) {
3090         DrawPosition(FALSE, NULL);
3091       }
3092     }
3093     dragInfo.start.x = dragInfo.start.y = -1; 
3094     dragInfo.pos = dragInfo.lastpos = dragInfo.start;
3095     break;
3096
3097   case WM_MOUSEMOVE:
3098     if ((appData.animateDragging || appData.highlightDragging)
3099         && (wParam & MK_LBUTTON)
3100         && dragInfo.from.x >= 0) {
3101       if (appData.animateDragging) {
3102         dragInfo.pos = pt;
3103       }
3104       if (appData.highlightDragging) {
3105         SetHighlights(fromX, fromY, x, y);
3106       }
3107       DrawPosition(FALSE, NULL);
3108       dragInfo.lastpos = dragInfo.pos;
3109     }
3110     break;
3111   case WM_MOUSEWHEEL:
3112         /* Mouse Wheel is being rolled forward 
3113          * Play moves forward
3114          */
3115         if ((short)HIWORD(wParam) > 0) 
3116            if (forwardMostMove > 0 && currentMove != forwardMostMove)
3117                    ForwardEvent();
3118            /* Mouse Wheel is being rolled backward 
3119             * Play moves backward
3120             */
3121         if ((short)HIWORD(wParam) < 0) 
3122            if (currentMove > 0) BackwardEvent();
3123         break;
3124   case WM_MBUTTONDOWN:
3125   case WM_RBUTTONDOWN:
3126     ErrorPopDown();
3127     ReleaseCapture();
3128     fromX = fromY = -1;
3129     dragInfo.pos.x = dragInfo.pos.y = -1;
3130     dragInfo.start.x = dragInfo.start.y = -1;
3131     dragInfo.from = dragInfo.start;
3132     dragInfo.lastpos = dragInfo.pos;
3133     if (appData.highlightDragging) {
3134       ClearHighlights();
3135     }
3136     DrawPosition(TRUE, NULL);
3137
3138     switch (gameMode) {
3139     case EditPosition:
3140     case IcsExamining:
3141       if (x < 0 || y < 0) break;
3142       fromX = x;
3143       fromY = y;
3144       if (message == WM_MBUTTONDOWN) {
3145         buttonCount = 3;  /* even if system didn't think so */
3146         if (wParam & MK_SHIFT) 
3147           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3148         else
3149           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3150       } else { /* message == WM_RBUTTONDOWN */
3151 #if 0
3152         if (buttonCount == 3) {
3153           if (wParam & MK_SHIFT) 
3154             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
3155           else
3156             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
3157         } else {
3158           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3159         }
3160 #else
3161         /* Just have one menu, on the right button.  Windows users don't
3162            think to try the middle one, and sometimes other software steals
3163            it, or it doesn't really exist. */
3164         MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
3165 #endif
3166       }
3167       break;
3168     case IcsPlayingWhite:
3169     case IcsPlayingBlack:
3170     case EditGame:
3171     case MachinePlaysWhite:
3172     case MachinePlaysBlack:
3173       if (appData.testLegality &&
3174           gameInfo.variant != VariantBughouse &&
3175           gameInfo.variant != VariantCrazyhouse) break;
3176       if (x < 0 || y < 0) break;
3177       fromX = x;
3178       fromY = y;
3179       hmenu = LoadMenu(hInst, "DropPieceMenu");
3180       SetupDropMenu(hmenu);
3181       MenuPopup(hwnd, pt, hmenu, -1);
3182       break;
3183     default:
3184       break;
3185     }
3186     break;
3187   }
3188
3189   recursive--;
3190 }
3191
3192 /* Preprocess messages for buttons in main window */
3193 LRESULT CALLBACK
3194 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3195 {
3196   int id = GetWindowLong(hwnd, GWL_ID);
3197   int i, dir;
3198
3199   for (i=0; i<N_BUTTONS; i++) {
3200     if (buttonDesc[i].id == id) break;
3201   }
3202   if (i == N_BUTTONS) return 0;
3203   switch (message) {
3204   case WM_KEYDOWN:
3205     switch (wParam) {
3206     case VK_LEFT:
3207     case VK_RIGHT:
3208       dir = (wParam == VK_LEFT) ? -1 : 1;
3209       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
3210       return TRUE;
3211     }
3212     break;
3213   case WM_CHAR:
3214     switch (wParam) {
3215     case '\r':
3216       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
3217       return TRUE;
3218     case '\t':
3219       if (appData.icsActive) {
3220         if (GetKeyState(VK_SHIFT) < 0) {
3221           /* shifted */
3222           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3223           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3224           SetFocus(h);
3225         } else {
3226           /* unshifted */
3227           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3228           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3229           SetFocus(h);
3230         }
3231         return TRUE;
3232       }
3233       break;
3234     default:
3235       if (appData.icsActive) {
3236         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3237         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3238         SetFocus(h);
3239         SendMessage(h, WM_CHAR, wParam, lParam);
3240         return TRUE;
3241       } else if (isalpha((char)wParam) || isdigit((char)wParam)){
3242         PopUpMoveDialog((char)wParam);
3243       }
3244       break;
3245     }
3246     break;
3247   }
3248   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
3249 }
3250
3251 /* Process messages for Promotion dialog box */
3252 LRESULT CALLBACK
3253 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
3254 {
3255   char promoChar;
3256
3257   switch (message) {
3258   case WM_INITDIALOG: /* message: initialize dialog box */
3259     /* Center the dialog over the application window */
3260     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
3261     ShowWindow(GetDlgItem(hDlg, PB_King), 
3262       (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3263        gameInfo.variant == VariantGiveaway) ?
3264                SW_SHOW : SW_HIDE);
3265     return TRUE;
3266
3267   case WM_COMMAND: /* message: received a command */
3268     switch (LOWORD(wParam)) {
3269     case IDCANCEL:
3270       EndDialog(hDlg, TRUE); /* Exit the dialog */
3271       ClearHighlights();
3272       DrawPosition(FALSE, NULL);
3273       return TRUE;
3274     case PB_King:
3275       promoChar = 'k';
3276       break;
3277     case PB_Queen:
3278       promoChar = 'q';
3279       break;
3280     case PB_Rook:
3281       promoChar = 'r';
3282       break;
3283     case PB_Bishop:
3284       promoChar = 'b';
3285       break;
3286     case PB_Knight:
3287       promoChar = 'n';
3288       break;
3289     default:
3290       return FALSE;
3291     }
3292     EndDialog(hDlg, TRUE); /* Exit the dialog */
3293     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3294     if (!appData.highlightLastMove) {
3295       ClearHighlights();
3296       DrawPosition(FALSE, NULL);
3297     }
3298     return TRUE;
3299   }
3300   return FALSE;
3301 }
3302
3303 /* Pop up promotion dialog */
3304 VOID
3305 PromotionPopup(HWND hwnd)
3306 {
3307   FARPROC lpProc;
3308
3309   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
3310   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
3311     hwnd, (DLGPROC)lpProc);
3312   FreeProcInstance(lpProc);
3313 }
3314
3315 /* Toggle ShowThinking */
3316 VOID
3317 ToggleShowThinking()
3318 {
3319   ShowThinkingEvent(!appData.showThinking);
3320 }
3321
3322 VOID
3323 LoadGameDialog(HWND hwnd, char* title)
3324 {
3325   UINT number = 0;
3326   FILE *f;
3327   char fileTitle[MSG_SIZ];
3328   f = OpenFileDialog(hwnd, FALSE, "",
3329                      appData.oldSaveStyle ? "gam" : "pgn",
3330                      GAME_FILT,
3331                      title, &number, fileTitle, NULL);
3332   if (f != NULL) {
3333     cmailMsgLoaded = FALSE;
3334     if (number == 0) {
3335       int error = GameListBuild(f);
3336       if (error) {
3337         DisplayError("Cannot build game list", error);
3338       } else if (!ListEmpty(&gameList) &&
3339                  ((ListGame *) gameList.tailPred)->number > 1) {
3340         GameListPopUp(f, fileTitle);
3341         return;
3342       }
3343       GameListDestroy();
3344       number = 1;
3345     }
3346     LoadGame(f, number, fileTitle, FALSE);
3347   }
3348 }
3349
3350 VOID
3351 ChangedConsoleFont()
3352 {
3353   CHARFORMAT cfmt;
3354   CHARRANGE tmpsel, sel;
3355   MyFont *f = font[boardSize][CONSOLE_FONT];
3356   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
3357   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3358   PARAFORMAT paraf;
3359
3360   cfmt.cbSize = sizeof(CHARFORMAT);
3361   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
3362   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
3363   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point
3364    * size.  This was undocumented in the version of MSVC++ that I had
3365    * when I wrote the code, but is apparently documented now.
3366    */
3367   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
3368   cfmt.bCharSet = f->lf.lfCharSet;
3369   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
3370   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); 
3371   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); 
3372   /* Why are the following seemingly needed too? */
3373   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); 
3374   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); 
3375   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
3376   tmpsel.cpMin = 0;
3377   tmpsel.cpMax = -1; /*999999?*/
3378   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
3379   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); 
3380   /* Trying putting this here too.  It still seems to tickle a RichEdit
3381    *  bug: sometimes RichEdit indents the first line of a paragraph too.
3382    */
3383   paraf.cbSize = sizeof(paraf);
3384   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
3385   paraf.dxStartIndent = 0;
3386   paraf.dxOffset = WRAP_INDENT;
3387   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);
3388   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
3389 }
3390
3391 /*---------------------------------------------------------------------------*\
3392  *
3393  * Window Proc for main window
3394  *
3395 \*---------------------------------------------------------------------------*/
3396
3397 /* Process messages for main window, etc. */
3398 LRESULT CALLBACK
3399 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3400 {
3401   FARPROC lpProc;
3402   int wmId, wmEvent;
3403   char *defName;
3404   FILE *f;
3405   UINT number;
3406   char fileTitle[MSG_SIZ];
3407
3408   switch (message) {
3409
3410   case WM_PAINT: /* message: repaint portion of window */
3411     PaintProc(hwnd);
3412     break;
3413
3414   case WM_ERASEBKGND:
3415     if (IsIconic(hwnd)) {
3416       /* Cheat; change the message */
3417       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
3418     } else {
3419       return (DefWindowProc(hwnd, message, wParam, lParam));
3420     }
3421     break;
3422
3423   case WM_LBUTTONDOWN:
3424   case WM_MBUTTONDOWN:
3425   case WM_RBUTTONDOWN:
3426   case WM_LBUTTONUP:
3427   case WM_MBUTTONUP:
3428   case WM_RBUTTONUP:
3429   case WM_MOUSEMOVE:
3430   case WM_MOUSEWHEEL:
3431     MouseEvent(hwnd, message, wParam, lParam);
3432     break;
3433
3434   case WM_CHAR:
3435     
3436     if (appData.icsActive) {
3437       if (wParam == '\t') {
3438         if (GetKeyState(VK_SHIFT) < 0) {
3439           /* shifted */
3440           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3441           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3442           SetFocus(h);
3443         } else {
3444           /* unshifted */
3445           HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
3446           if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3447           SetFocus(h);
3448         }
3449       } else {
3450         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
3451         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
3452         SetFocus(h);
3453         SendMessage(h, message, wParam, lParam);
3454       }
3455     } else if (isalpha((char)wParam) || isdigit((char)wParam)) {
3456       PopUpMoveDialog((char)wParam);
3457     }
3458     break;
3459
3460   case WM_PALETTECHANGED:
3461     if (hwnd != (HWND)wParam && !appData.monoMode) {
3462       int nnew;
3463       HDC hdc = GetDC(hwndMain);
3464       SelectPalette(hdc, hPal, TRUE);
3465       nnew = RealizePalette(hdc);
3466       if (nnew > 0) {
3467         paletteChanged = TRUE;
3468 #if 0
3469         UpdateColors(hdc);
3470 #else
3471         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/
3472 #endif
3473       }
3474       ReleaseDC(hwnd, hdc);
3475     }
3476     break;
3477
3478   case WM_QUERYNEWPALETTE:
3479     if (!appData.monoMode /*&& paletteChanged*/) {
3480       int nnew;
3481       HDC hdc = GetDC(hwndMain);
3482       paletteChanged = FALSE;
3483       SelectPalette(hdc, hPal, FALSE);
3484       nnew = RealizePalette(hdc);
3485       if (nnew > 0) {
3486         InvalidateRect(hwnd, &boardRect, FALSE);
3487       }
3488       ReleaseDC(hwnd, hdc);
3489       return TRUE;
3490     }
3491     return FALSE;
3492
3493   case WM_COMMAND: /* message: command from application menu */
3494     wmId    = LOWORD(wParam);
3495     wmEvent = HIWORD(wParam);
3496
3497     switch (wmId) {
3498     case IDM_NewGame:
3499       ResetGameEvent();
3500       AnalysisPopDown();
3501       break;
3502
3503     case IDM_LoadGame:
3504       LoadGameDialog(hwnd, "Load Game from File");
3505       break;
3506
3507     case IDM_LoadNextGame:
3508       ReloadGame(1);
3509       break;
3510
3511     case IDM_LoadPrevGame:
3512       ReloadGame(-1);
3513       break;
3514
3515     case IDM_ReloadGame:
3516       ReloadGame(0);
3517       break;
3518
3519     case IDM_LoadPosition:
3520       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
3521         Reset(FALSE, TRUE);
3522       }
3523       number = 1;
3524       f = OpenFileDialog(hwnd, FALSE, "",
3525                          appData.oldSaveStyle ? "pos" : "fen",
3526                          POSITION_FILT,
3527                          "Load Position from File", &number, fileTitle, NULL);
3528       if (f != NULL) {
3529         LoadPosition(f, number, fileTitle);
3530       }
3531       break;
3532
3533     case IDM_LoadNextPosition:
3534       ReloadPosition(1);
3535       break;
3536
3537     case IDM_LoadPrevPosition:
3538       ReloadPosition(-1);
3539       break;
3540
3541     case IDM_ReloadPosition:
3542       ReloadPosition(0);
3543       break;
3544
3545     case IDM_SaveGame:
3546       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
3547       f = OpenFileDialog(hwnd, TRUE, defName,
3548                          appData.oldSaveStyle ? "gam" : "pgn",
3549                          GAME_FILT,
3550                          "Save Game to File", NULL, fileTitle, NULL);
3551       if (f != NULL) {
3552         SaveGame(f, 0, "");
3553       }
3554       break;
3555
3556     case IDM_SavePosition:
3557       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
3558       f = OpenFileDialog(hwnd, TRUE, defName,
3559                          appData.oldSaveStyle ? "pos" : "fen",
3560                          POSITION_FILT,
3561                          "Save Position to File", NULL, fileTitle, NULL);
3562       if (f != NULL) {
3563         SavePosition(f, 0, "");
3564       }
3565       break;
3566
3567     case IDM_CopyGame:
3568       CopyGameToClipboard();
3569       break;
3570
3571     case IDM_PasteGame:
3572       PasteGameFromClipboard();
3573       break;
3574
3575     case IDM_CopyPosition:
3576       CopyFENToClipboard();
3577       break;
3578
3579     case IDM_PastePosition:
3580       PasteFENFromClipboard();
3581       break;
3582
3583     case IDM_MailMove:
3584       MailMoveEvent();
3585       break;
3586
3587     case IDM_ReloadCMailMsg:
3588       Reset(TRUE, TRUE);
3589       ReloadCmailMsgEvent(FALSE);
3590       break;
3591
3592     case IDM_Minimize:
3593       ShowWindow(hwnd, SW_MINIMIZE);
3594       break;
3595
3596     case IDM_Exit:
3597       ExitEvent(0);
3598       break;
3599
3600     case IDM_MachineWhite:
3601       MachineWhiteEvent();
3602       /*
3603        * refresh the tags dialog only if it's visible
3604        */
3605       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
3606           char *tags;
3607           tags = PGNTags(&gameInfo);
3608           TagsPopUp(tags, CmailMsg());
3609           free(tags);
3610       }
3611       break;
3612
3613     case IDM_MachineBlack:
3614       MachineBlackEvent();
3615       /*
3616        * refresh the tags dialog only if it's visible
3617        */
3618       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
3619           char *tags;
3620           tags = PGNTags(&gameInfo);
3621           TagsPopUp(tags, CmailMsg());
3622           free(tags);
3623       }
3624       break;
3625
3626     case IDM_TwoMachines:
3627       TwoMachinesEvent();
3628       /*
3629        * refresh the tags dialog only if it's visible
3630        */
3631       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
3632           char *tags;
3633           tags = PGNTags(&gameInfo);
3634           TagsPopUp(tags, CmailMsg());
3635           free(tags);
3636       }
3637       break;
3638
3639     case IDM_AnalysisMode:
3640       if (!first.analysisSupport) {
3641         char buf[MSG_SIZ];
3642         sprintf(buf, "%s does not support analysis", first.tidy);
3643         DisplayError(buf, 0);
3644       } else {
3645         if (!appData.showThinking) ToggleShowThinking();
3646         AnalyzeModeEvent();
3647       }
3648       break;
3649
3650     case IDM_AnalyzeFile:
3651       if (!first.analysisSupport) {
3652         char buf[MSG_SIZ];
3653         sprintf(buf, "%s does not support analysis", first.tidy);
3654         DisplayError(buf, 0);
3655       } else {
3656         if (!appData.showThinking) ToggleShowThinking();
3657         AnalyzeFileEvent();
3658         LoadGameDialog(hwnd, "Analyze Game from File");
3659         AnalysisPeriodicEvent(1);
3660       }
3661       break;
3662
3663     case IDM_IcsClient:
3664       IcsClientEvent();
3665       break;
3666
3667     case IDM_EditGame:
3668       EditGameEvent();
3669       break;
3670
3671     case IDM_EditPosition:
3672       EditPositionEvent();
3673       break;
3674
3675     case IDM_Training:
3676       TrainingEvent();
3677       break;
3678
3679     case IDM_ShowGameList:
3680       ShowGameListProc();
3681       break;
3682
3683     case IDM_EditTags:
3684       EditTagsProc();
3685       break;
3686
3687     case IDM_EditComment:
3688       if (commentDialogUp && editComment) {
3689         CommentPopDown();
3690       } else {
3691         EditCommentEvent();
3692       }
3693       break;
3694
3695     case IDM_Pause:
3696       PauseEvent();
3697       break;
3698
3699     case IDM_Accept:
3700       AcceptEvent();
3701       break;
3702
3703     case IDM_Decline:
3704       DeclineEvent();
3705       break;
3706
3707     case IDM_Rematch:
3708       RematchEvent();
3709       break;
3710
3711     case IDM_CallFlag:
3712       CallFlagEvent();
3713       break;
3714
3715     case IDM_Draw:
3716       DrawEvent();
3717       break;
3718
3719     case IDM_Adjourn:
3720       AdjournEvent();
3721       break;
3722
3723     case IDM_Abort:
3724       AbortEvent();
3725       break;
3726
3727     case IDM_Resign:
3728       ResignEvent();
3729       break;
3730
3731     case IDM_StopObserving:
3732       StopObservingEvent();
3733       break;
3734
3735     case IDM_StopExamining:
3736       StopExaminingEvent();
3737       break;
3738
3739     case IDM_TypeInMove:
3740       PopUpMoveDialog('\000');
3741       break;
3742
3743     case IDM_Backward:
3744       BackwardEvent();
3745       SetFocus(hwndMain);
3746       break;
3747
3748     case IDM_Forward:
3749       ForwardEvent();
3750       SetFocus(hwndMain);
3751       break;
3752
3753     case IDM_ToStart:
3754       ToStartEvent();
3755       SetFocus(hwndMain);
3756       break;
3757
3758     case IDM_ToEnd:
3759       ToEndEvent();
3760       SetFocus(hwndMain);
3761       break;
3762
3763     case IDM_Revert:
3764       RevertEvent();
3765       break;
3766
3767     case IDM_TruncateGame:
3768       TruncateGameEvent();
3769       break;
3770
3771     case IDM_MoveNow:
3772       MoveNowEvent();
3773       break;
3774
3775     case IDM_RetractMove:
3776       RetractMoveEvent();
3777       break;
3778
3779     case IDM_FlipView:
3780       flipView = !flipView;
3781       DrawPosition(FALSE, NULL);
3782       break;
3783
3784     case IDM_GeneralOptions:
3785       GeneralOptionsPopup(hwnd);
3786       break;
3787
3788     case IDM_BoardOptions:
3789       BoardOptionsPopup(hwnd);
3790       break;
3791
3792     case IDM_IcsOptions:
3793       IcsOptionsPopup(hwnd);
3794       break;
3795
3796     case IDM_Fonts:
3797       FontsOptionsPopup(hwnd);
3798       break;
3799
3800     case IDM_Sounds:
3801       SoundOptionsPopup(hwnd);
3802       break;
3803
3804     case IDM_CommPort:
3805       CommPortOptionsPopup(hwnd);
3806       break;
3807
3808     case IDM_LoadOptions:
3809       LoadOptionsPopup(hwnd);
3810       break;
3811
3812     case IDM_SaveOptions:
3813       SaveOptionsPopup(hwnd);
3814       break;
3815
3816     case IDM_TimeControl:
3817       TimeControlOptionsPopup(hwnd);
3818       break;
3819
3820     case IDM_SaveSettings:
3821       SaveSettings(settingsFileName);
3822       break;
3823
3824     case IDM_SaveSettingsOnExit:
3825       saveSettingsOnExit = !saveSettingsOnExit;
3826       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
3827                            MF_BYCOMMAND|(saveSettingsOnExit ?
3828                                          MF_CHECKED : MF_UNCHECKED));
3829       break;
3830
3831     case IDM_Hint:
3832       HintEvent();
3833       break;
3834
3835     case IDM_Book:
3836       BookEvent();
3837       break;
3838
3839     case IDM_AboutGame:
3840       AboutGameEvent();
3841       break;
3842
3843     case IDM_Debug:
3844       appData.debugMode = !appData.debugMode;
3845       if (appData.debugMode) {
3846         char dir[MSG_SIZ];
3847         GetCurrentDirectory(MSG_SIZ, dir);
3848         SetCurrentDirectory(installDir);
3849         debugFP = fopen("WinBoard.debug", "w");
3850         SetCurrentDirectory(dir);
3851         setbuf(debugFP, NULL);
3852       } else {
3853         fclose(debugFP);
3854         debugFP = NULL;
3855       }
3856       break;
3857
3858     case IDM_HELPCONTENTS:
3859       if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
3860         MessageBox (GetFocus(),
3861                     "Unable to activate help",
3862                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3863       }
3864       break;
3865
3866     case IDM_HELPSEARCH:
3867       if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
3868         MessageBox (GetFocus(),
3869                     "Unable to activate help",
3870                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3871       }
3872       break;
3873
3874     case IDM_HELPHELP:
3875       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
3876         MessageBox (GetFocus(),
3877                     "Unable to activate help",
3878                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
3879       }
3880       break;
3881
3882     case IDM_ABOUT:
3883       lpProc = MakeProcInstance((FARPROC)About, hInst);
3884       DialogBox(hInst, 
3885         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
3886         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
3887       FreeProcInstance(lpProc);
3888       break;
3889
3890     case IDM_DirectCommand1:
3891       AskQuestionEvent("Direct Command",
3892                        "Send to chess program:", "", "1");
3893       break;
3894     case IDM_DirectCommand2:
3895       AskQuestionEvent("Direct Command",
3896                        "Send to second chess program:", "", "2");
3897       break;
3898
3899     case EP_WhitePawn:
3900       EditPositionMenuEvent(WhitePawn, fromX, fromY);
3901       fromX = fromY = -1;
3902       break;
3903
3904     case EP_WhiteKnight:
3905       EditPositionMenuEvent(WhiteKnight, fromX, fromY);
3906       fromX = fromY = -1;
3907       break;
3908
3909     case EP_WhiteBishop:
3910       EditPositionMenuEvent(WhiteBishop, fromX, fromY);
3911       fromX = fromY = -1;
3912       break;
3913
3914     case EP_WhiteRook:
3915       EditPositionMenuEvent(WhiteRook, fromX, fromY);
3916       fromX = fromY = -1;
3917       break;
3918
3919     case EP_WhiteQueen:
3920       EditPositionMenuEvent(WhiteQueen, fromX, fromY);
3921       fromX = fromY = -1;
3922       break;
3923
3924     case EP_WhiteKing:
3925       EditPositionMenuEvent(WhiteKing, fromX, fromY);
3926       fromX = fromY = -1;
3927       break;
3928
3929     case EP_BlackPawn:
3930       EditPositionMenuEvent(BlackPawn, fromX, fromY);
3931       fromX = fromY = -1;
3932       break;
3933
3934     case EP_BlackKnight:
3935       EditPositionMenuEvent(BlackKnight, fromX, fromY);
3936       fromX = fromY = -1;
3937       break;
3938
3939     case EP_BlackBishop:
3940       EditPositionMenuEvent(BlackBishop, fromX, fromY);
3941       fromX = fromY = -1;
3942       break;
3943
3944     case EP_BlackRook:
3945       EditPositionMenuEvent(BlackRook, fromX, fromY);
3946       fromX = fromY = -1;
3947       break;
3948
3949     case EP_BlackQueen:
3950       EditPositionMenuEvent(BlackQueen, fromX, fromY);
3951       fromX = fromY = -1;
3952       break;
3953
3954     case EP_BlackKing:
3955       EditPositionMenuEvent(BlackKing, fromX, fromY);
3956       fromX = fromY = -1;
3957       break;
3958
3959     case EP_EmptySquare:
3960       EditPositionMenuEvent(EmptySquare, fromX, fromY);
3961       fromX = fromY = -1;
3962       break;
3963
3964     case EP_ClearBoard:
3965       EditPositionMenuEvent(ClearBoard, fromX, fromY);
3966       fromX = fromY = -1;
3967       break;
3968
3969     case EP_White:
3970       EditPositionMenuEvent(WhitePlay, fromX, fromY);
3971       fromX = fromY = -1;
3972       break;
3973
3974     case EP_Black:
3975       EditPositionMenuEvent(BlackPlay, fromX, fromY);
3976       fromX = fromY = -1;
3977       break;
3978
3979     case DP_Pawn:
3980       DropMenuEvent(WhitePawn, fromX, fromY);
3981       fromX = fromY = -1;
3982       break;
3983
3984     case DP_Knight:
3985       DropMenuEvent(WhiteKnight, fromX, fromY);
3986       fromX = fromY = -1;
3987       break;
3988
3989     case DP_Bishop:
3990       DropMenuEvent(WhiteBishop, fromX, fromY);
3991       fromX = fromY = -1;
3992       break;
3993
3994     case DP_Rook:
3995       DropMenuEvent(WhiteRook, fromX, fromY);
3996       fromX = fromY = -1;
3997       break;
3998
3999     case DP_Queen:
4000       DropMenuEvent(WhiteQueen, fromX, fromY);
4001       fromX = fromY = -1;
4002       break;
4003
4004     default:
4005       return (DefWindowProc(hwnd, message, wParam, lParam));
4006     }
4007     break;
4008
4009   case WM_TIMER:
4010     switch (wParam) {
4011     case CLOCK_TIMER_ID:
4012       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */
4013       clockTimerEvent = 0;
4014       DecrementClocks(); /* call into back end */
4015       break;
4016     case LOAD_GAME_TIMER_ID:
4017       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
4018       loadGameTimerEvent = 0;
4019       AutoPlayGameLoop(); /* call into back end */
4020       break;
4021     case ANALYSIS_TIMER_ID:
4022       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && 
4023           appData.periodicUpdates) {
4024         AnalysisPeriodicEvent(0);
4025       } else {
4026         KillTimer(hwnd, analysisTimerEvent);
4027         analysisTimerEvent = 0;
4028       }
4029       break;
4030     case DELAYED_TIMER_ID:
4031       KillTimer(hwnd, delayedTimerEvent);
4032       delayedTimerEvent = 0;
4033       delayedTimerCallback();
4034       break;
4035     }
4036     break;
4037
4038   case WM_USER_Input:
4039     InputEvent(hwnd, message, wParam, lParam);
4040     break;
4041
4042   case WM_ENTERSIZEMOVE:
4043     if (hwnd == hwndMain) {
4044       doingSizing = TRUE;
4045       lastSizing = 0;
4046     }
4047     break;
4048
4049   case WM_SIZING:
4050     if (hwnd == hwndMain) {
4051       lastSizing = wParam;
4052     }
4053     break;
4054
4055   case WM_EXITSIZEMOVE:
4056     if (hwnd == hwndMain) {
4057       RECT client;
4058       doingSizing = FALSE;
4059       InvalidateRect(hwnd, &boardRect, FALSE);
4060       GetClientRect(hwnd, &client);
4061       ResizeBoard(client.right, client.bottom, lastSizing);
4062       lastSizing = 0;
4063     }
4064     break;
4065
4066   case WM_DESTROY: /* message: window being destroyed */
4067     PostQuitMessage(0);
4068     break;
4069
4070   case WM_CLOSE:
4071     if (hwnd == hwndMain) {
4072       ExitEvent(0);
4073     }
4074     break;
4075
4076   default:      /* Passes it on if unprocessed */
4077     return (DefWindowProc(hwnd, message, wParam, lParam));
4078   }
4079   return 0;
4080 }
4081
4082 /*---------------------------------------------------------------------------*\
4083  *
4084  * Misc utility routines
4085  *
4086 \*---------------------------------------------------------------------------*/
4087
4088 /*
4089  * Decent random number generator, at least not as bad as Windows
4090  * standard rand, which returns a value in the range 0 to 0x7fff.
4091  */
4092 unsigned int randstate;
4093
4094 int
4095 myrandom(void)
4096 {
4097   randstate = randstate * 1664525 + 1013904223;
4098   return (int) randstate & 0x7fffffff;
4099 }
4100
4101 void
4102 mysrandom(unsigned int seed)
4103 {
4104   randstate = seed;
4105 }
4106
4107
4108 /* 
4109  * returns TRUE if user selects a different color, FALSE otherwise 
4110  */
4111
4112 BOOL
4113 ChangeColor(HWND hwnd, COLORREF *which)
4114 {
4115   static BOOL firstTime = TRUE;
4116   static DWORD customColors[16];
4117   CHOOSECOLOR cc;
4118   COLORREF newcolor;
4119   int i;
4120   ColorClass ccl;
4121
4122   if (firstTime) {
4123     /* Make initial colors in use available as custom colors */
4124     /* Should we put the compiled-in defaults here instead? */
4125     i = 0;
4126     customColors[i++] = lightSquareColor & 0xffffff;
4127     customColors[i++] = darkSquareColor & 0xffffff;
4128     customColors[i++] = whitePieceColor & 0xffffff;
4129     customColors[i++] = blackPieceColor & 0xffffff;
4130     customColors[i++] = highlightSquareColor & 0xffffff;
4131     customColors[i++] = premoveHighlightColor & 0xffffff;
4132
4133     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
4134       customColors[i++] = textAttribs[ccl].color;
4135     }
4136     while (i < 16) customColors[i++] = RGB(255, 255, 255);
4137     firstTime = FALSE;
4138   }
4139
4140   cc.lStructSize = sizeof(cc);
4141   cc.hwndOwner = hwnd;
4142   cc.hInstance = NULL;
4143   cc.rgbResult = (DWORD) (*which & 0xffffff);
4144   cc.lpCustColors = (LPDWORD) customColors;
4145   cc.Flags = CC_RGBINIT|CC_FULLOPEN;
4146
4147   if (!ChooseColor(&cc)) return FALSE;
4148
4149   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
4150   if (newcolor == *which) return FALSE;
4151   *which = newcolor;
4152   return TRUE;
4153
4154   /*
4155   InitDrawingColors();
4156   InvalidateRect(hwnd, &boardRect, FALSE);
4157   */
4158 }
4159
4160 BOOLEAN
4161 MyLoadSound(MySound *ms)
4162 {
4163   BOOL ok = FALSE;
4164   struct stat st;
4165   FILE *f;
4166
4167   if (ms->data) free(ms->data);
4168   ms->data = NULL;
4169
4170   switch (ms->name[0]) {
4171   case NULLCHAR:
4172     /* Silence */
4173     ok = TRUE;
4174     break;
4175   case '$':
4176     /* System sound from Control Panel.  Don't preload here. */
4177     ok = TRUE;
4178     break;
4179   case '!':
4180     if (ms->name[1] == NULLCHAR) {
4181       /* "!" alone = silence */
4182       ok = TRUE;
4183     } else {
4184       /* Builtin wave resource.  Error if not found. */
4185       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
4186       if (h == NULL) break;
4187       ms->data = (void *)LoadResource(hInst, h);
4188       if (h == NULL) break;
4189       ok = TRUE;
4190     }
4191     break;
4192   default:
4193     /* .wav file.  Error if not found. */
4194     f = fopen(ms->name, "rb");
4195     if (f == NULL) break;
4196     if (fstat(fileno(f), &st) < 0) break;
4197     ms->data = malloc(st.st_size);
4198     if (fread(ms->data, st.st_size, 1, f) < 1) break;
4199     fclose(f);
4200     ok = TRUE;
4201     break;
4202   }
4203   if (!ok) {
4204     char buf[MSG_SIZ];
4205     sprintf(buf, "Error loading sound %s", ms->name);
4206     DisplayError(buf, GetLastError());
4207   }
4208   return ok;
4209 }
4210
4211 BOOLEAN
4212 MyPlaySound(MySound *ms)
4213 {
4214   BOOLEAN ok = FALSE;
4215   switch (ms->name[0]) {
4216   case NULLCHAR:
4217     /* Silence */
4218     ok = TRUE;
4219     break;
4220   case '$':
4221     /* System sound from Control Panel (deprecated feature).
4222        "$" alone or an unset sound name gets default beep (still in use). */
4223     if (ms->name[1]) {
4224       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
4225     }
4226     if (!ok) ok = MessageBeep(MB_OK);
4227     break; 
4228   case '!':
4229     /* Builtin wave resource, or "!" alone for silence */
4230     if (ms->name[1]) {
4231       if (ms->data == NULL) return FALSE;
4232       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
4233     } else {
4234       ok = TRUE;