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