mehrmann wb extensions
[xboard.git] / winboard-dm-beta4 / winboard.c
diff --git a/winboard-dm-beta4/winboard.c b/winboard-dm-beta4/winboard.c
new file mode 100755 (executable)
index 0000000..c0f7a0c
--- /dev/null
@@ -0,0 +1,8401 @@
+/* 
+ * WinBoard.c -- Windows NT front end to XBoard
+ * $Id$
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
+ * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
+ *
+ * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,
+ * which was written and is copyrighted by Wayne Christopher.
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * The following terms apply to the enhanced version of XBoard distributed
+ * by the Free Software Foundation:
+ * ------------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ------------------------------------------------------------------------
+ */
+
+#include "config.h"
+
+#include <windows.h>
+#include <winuser.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+#include <commdlg.h>
+#include <dlgs.h>
+#include <richedit.h>
+
+/* Need for Shell */
+#include <shellapi.h>
+
+#if __GNUC__
+#include <errno.h>
+#include <string.h>
+#endif
+
+#include "common.h"
+#include "winboard.h"
+#include "frontend.h"
+#include "backend.h"
+#include "moves.h"
+#include "wclipbrd.h"
+#include "wgamelist.h"
+#include "wedittags.h"
+#include "woptions.h"
+#include "wsockerr.h"
+#include "defaults.h"
+
+  char *tit;
+  char *pvs;
+  char *dep;
+  char *np;
+  char *nod;
+  char *sco;
+
+typedef struct {
+  ChessSquare piece;  
+  POINT pos;      /* window coordinates of current pos */
+  POINT lastpos;  /* window coordinates of last pos - used for clipping */
+  POINT from;     /* board coordinates of the piece's orig pos */
+  POINT to;       /* board coordinates of the piece's new pos */
+} AnimInfo;
+
+static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };
+
+typedef struct {
+  POINT start;    /* window coordinates of start pos */
+  POINT pos;      /* window coordinates of current pos */
+  POINT lastpos;  /* window coordinates of last pos - used for clipping */
+  POINT from;     /* board coordinates of the piece's orig pos */
+} DragInfo;
+
+static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };
+
+typedef struct {
+  POINT sq[2];   /* board coordinates of from, to squares */
+} HighlightInfo;
+
+static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };
+static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };
+
+/* Window class names */
+char szAppName[] = "WinBoard";
+char szConsoleName[] = "WBConsole";
+
+/* Title bar text */
+char szTitle[] = "WinBoard";
+char szConsoleTitle[] = "ICS Interaction";
+
+char *programName;
+char *settingsFileName;
+BOOLEAN saveSettingsOnExit;
+char installDir[MSG_SIZ];
+
+BoardSize boardSize;
+BOOLEAN chessProgram;
+static int boardX, boardY, consoleX, consoleY, consoleW, consoleH;
+static int squareSize, lineGap;
+static int winWidth, winHeight;
+static RECT messageRect, whiteRect, blackRect;
+static char messageText[MESSAGE_TEXT_MAX];
+static int clockTimerEvent = 0;
+static int loadGameTimerEvent = 0;
+static DelayedEventCallback delayedTimerCallback;
+static int delayedTimerEvent = 0;
+static int buttonCount = 2;
+char *icsTextMenuString;
+char *icsNames;
+char *firstChessProgramNames;
+char *secondChessProgramNames;
+
+static int analysisTimerEvent = 0;
+
+#define ARG_MAX 20000
+
+#define PALETTESIZE 256
+
+/* GUI -> engine */
+extern void GuiCommand P((int command, int param));
+
+HINSTANCE hInst;          /* current instance */
+HWND hwndMain = NULL;        /* root window*/
+HWND hwndConsole = NULL;
+
+BOOLEAN alwaysOnTop = FALSE;
+RECT boardRect;
+COLORREF lightSquareColor, darkSquareColor, whitePieceColor, 
+  blackPieceColor, highlightSquareColor, premoveHighlightColor;
+HPALETTE hPal;
+ColorClass currentColorClass;
+
+HWND hCommPort = NULL;    /* currently open comm port */
+static HWND hwndPause;    /* pause button */
+static HBITMAP pieceBitmap[3][(int) WhiteKing + 1];
+static HBRUSH lightSquareBrush, darkSquareBrush,
+  whitePieceBrush, blackPieceBrush, iconBkgndBrush, outlineBrush;
+static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];
+static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];
+static HPEN gridPen = NULL;
+static HPEN highlightPen = NULL;
+static HPEN premovePen = NULL;
+static NPLOGPALETTE pLogPal;
+static BOOL paletteChanged = FALSE;
+static HICON iconWhite, iconBlack, iconCurrent;
+static int doingSizing = FALSE;
+static int lastSizing = 0;
+
+#if __GNUC__ && !defined(_winmajor)
+#define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */
+#else
+#define oldDialog (_winmajor < 4)
+#endif
+
+char *defaultTextAttribs[] = 
+{
+  COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,
+  COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,
+  COLOR_NONE
+};
+
+typedef struct {
+  char *name;
+  int squareSize;
+  int lineGap;
+  int smallLayout;
+  int tinyLayout;
+  int cliWidth, cliHeight;
+} SizeInfo;
+
+SizeInfo sizeInfo[] = 
+{
+  { "tiny",     21, 0, 1, 1, 0, 0 },
+  { "teeny",    25, 1, 1, 1, 0, 0 },
+  { "dinky",    29, 1, 1, 1, 0, 0 },
+  { "petite",   33, 1, 1, 1, 0, 0 },
+  { "slim",     37, 2, 1, 0, 0, 0 },
+  { "small",    40, 2, 1, 0, 0, 0 },
+  { "mediocre", 45, 2, 1, 0, 0, 0 },
+  { "middling", 49, 2, 0, 0, 0, 0 },
+  { "average",  54, 2, 0, 0, 0, 0 },
+  { "moderate", 58, 3, 0, 0, 0, 0 },
+  { "medium",   64, 3, 0, 0, 0, 0 },
+  { "bulky",    72, 3, 0, 0, 0, 0 },
+  { "large",    80, 3, 0, 0, 0, 0 },
+  { "big",      87, 3, 0, 0, 0, 0 },
+  { "huge",     95, 3, 0, 0, 0, 0 },
+  { "giant",    108, 3, 0, 0, 0, 0 },
+  { "colossal", 116, 4, 0, 0, 0, 0 },
+  { "titanic",  129, 4, 0, 0, 0, 0 },
+  { NULL, 0, 0, 0, 0, 0, 0 }
+};
+
+#define MF(x) {x, {0, }, {0, }, 0}
+MyFont fontRec[NUM_SIZES][NUM_FONTS] =
+{
+  { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), 
+    MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY),
+    MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY) },
+  { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), 
+    MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY),
+    MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY) },
+  { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY),
+    MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY),
+    MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY) },
+  { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE),
+    MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE),
+    MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE) },
+  { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM),
+    MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM),
+    MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM) },
+  { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL),
+    MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL),
+    MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL) },
+  { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE),
+    MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE),
+    MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE) },
+  { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING),
+    MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING),
+    MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING) },
+  { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE),
+    MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE),
+    MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE) },
+  { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE),
+    MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE),
+    MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE) },
+  { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM),
+    MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM),
+    MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM) },
+  { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY),
+    MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY),
+    MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY) },
+  { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE),
+    MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE),
+    MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE) },
+  { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG),
+    MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG),
+    MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG) },
+  { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE),
+    MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE),
+    MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE) },
+  { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT),
+    MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT),
+    MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT) },
+  { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL),
+    MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL),
+    MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL) },
+  { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC),
+    MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC),
+    MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC) },
+};
+
+MyFont *font[NUM_SIZES][NUM_FONTS];
+
+typedef struct {
+  char *label;
+  int id;
+  HWND hwnd;
+  WNDPROC wndproc;
+} MyButtonDesc;
+
+#define BUTTON_WIDTH (tinyLayout ? 16 : 32)
+#define N_BUTTONS 5
+
+MyButtonDesc buttonDesc[N_BUTTONS] =
+{
+  {"<<", IDM_ToStart, NULL, NULL},
+  {"<", IDM_Backward, NULL, NULL},
+  {"P", IDM_Pause, NULL, NULL},
+  {">", IDM_Forward, NULL, NULL},
+  {">>", IDM_ToEnd, NULL, NULL},
+};
+
+int tinyLayout = 0, smallLayout = 0;
+#define MENU_BAR_ITEMS 6
+char *menuBarText[2][MENU_BAR_ITEMS+1] = {
+  { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },
+  { "&F", "&M", "&A", "&S", "&O", "&H", NULL },
+};
+
+
+MySound sounds[(int)NSoundClasses];
+MyTextAttribs textAttribs[(int)NColorClasses];
+
+MyColorizeAttribs colorizeAttribs[] = {
+  { (COLORREF)0, 0, "Shout Text" },
+  { (COLORREF)0, 0, "SShout/CShout" },
+  { (COLORREF)0, 0, "Channel 1 Text" },
+  { (COLORREF)0, 0, "Channel Text" },
+  { (COLORREF)0, 0, "Kibitz Text" },
+  { (COLORREF)0, 0, "Tell Text" },
+  { (COLORREF)0, 0, "Challenge Text" },
+  { (COLORREF)0, 0, "Request Text" },
+  { (COLORREF)0, 0, "Seek Text" },
+  { (COLORREF)0, 0, "Normal Text" },
+  { (COLORREF)0, 0, "None" }
+};
+
+
+
+static char *commentTitle;
+static char *commentText;
+static int commentIndex;
+static Boolean editComment = FALSE;
+HWND commentDialog = NULL;
+BOOLEAN commentDialogUp = FALSE;
+static int commentX, commentY, commentH, commentW;
+
+HWND analysisDialog = NULL;
+BOOLEAN analysisDialogUp = FALSE;
+static int analysisX, analysisY, analysisH, analysisW;
+
+char errorMessage[2*MSG_SIZ];
+HWND errorDialog = NULL;
+BOOLEAN moveErrorMessageUp = FALSE;
+BOOLEAN consoleEcho = TRUE;
+CHARFORMAT consoleCF;
+COLORREF consoleBackgroundColor;
+
+char *programVersion;
+
+#include <winsock.h>
+
+#define CPReal 1
+#define CPComm 2
+#define CPSock 3
+#define CPRcmd 4
+typedef int CPKind;
+
+typedef struct {
+  CPKind kind;
+  HANDLE hProcess;
+  DWORD pid;
+  HANDLE hTo;
+  HANDLE hFrom;
+  SOCKET sock;
+  SOCKET sock2;  /* stderr socket for OpenRcmd */
+} ChildProc;
+
+#define INPUT_SOURCE_BUF_SIZE 4096
+
+typedef struct _InputSource {
+  CPKind kind;
+  HANDLE hFile;
+  SOCKET sock;
+  int lineByLine;
+  HANDLE hThread;
+  DWORD id;
+  char buf[INPUT_SOURCE_BUF_SIZE];
+  char *next;
+  DWORD count;
+  int error;
+  InputCallback func;
+  struct _InputSource *second;  /* for stderr thread on CPRcmd */
+  VOIDSTAR closure;
+} InputSource;
+
+InputSource *consoleInputSource;
+
+DCB dcb;
+
+/* forward */
+VOID ConsoleOutput(char* data, int length, int forceVisible);
+VOID ConsoleCreate();
+LRESULT CALLBACK
+  ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK
+  AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);
+VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);
+VOID ParseCommSettings(char *arg, DCB *dcb);
+
+LRESULT CALLBACK
+  StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);
+void ParseIcsTextMenu(char *icsTextMenuString);
+VOID PopUpMoveDialog(char firstchar);
+VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);
+
+/*
+ * Setting "frozen" should disable all user input other than deleting
+ * the window.  We do this while engines are initializing themselves.
+ */
+static int frozen = 0;
+static int oldMenuItemState[MENU_BAR_ITEMS];
+void FreezeUI()
+{
+  HMENU hmenu;
+  int i;
+
+  if (frozen) return;
+  frozen = 1;
+  hmenu = GetMenu(hwndMain);
+  for (i=0; i<MENU_BAR_ITEMS; i++) {
+    oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);
+  }
+  DrawMenuBar(hwndMain);
+}
+
+/* Undo a FreezeUI */
+void ThawUI()
+{
+  HMENU hmenu;
+  int i;
+
+  if (!frozen) return;
+  frozen = 0;
+  hmenu = GetMenu(hwndMain);
+  for (i=0; i<MENU_BAR_ITEMS; i++) {
+    EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);
+  }
+  DrawMenuBar(hwndMain);
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ * WinMain
+ *
+\*---------------------------------------------------------------------------*/
+
+int APIENTRY
+WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+       LPSTR lpCmdLine, int nCmdShow)
+{
+  MSG msg;
+  HANDLE hAccelMain, hAccelNoAlt;
+
+  debugFP = stderr;
+
+  LoadLibrary("RICHED32.DLL");
+  consoleCF.cbSize = sizeof(CHARFORMAT);
+  if (!InitApplication(hInstance)) {
+    return (FALSE);
+  }
+  if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {
+    return (FALSE);
+  }
+
+  hAccelMain = LoadAccelerators (hInstance, szAppName);
+  hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");
+
+  /* Acquire and dispatch messages until a WM_QUIT message is received. */
+
+  while (GetMessage(&msg, /* message structure */
+                   NULL, /* handle of window receiving the message */
+                   0,    /* lowest message to examine */
+                   0))   /* highest message to examine */
+    {
+      if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&
+         !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&
+         !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&
+         !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&
+         !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&
+         !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {
+       TranslateMessage(&msg); /* Translates virtual key codes */
+       DispatchMessage(&msg);  /* Dispatches message to window */
+      }
+    }
+
+
+  return (msg.wParam); /* Returns the value from PostQuitMessage */
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ * Initialization functions
+ *
+\*---------------------------------------------------------------------------*/
+
+BOOL
+InitApplication(HINSTANCE hInstance)
+{
+  WNDCLASS wc;
+
+  /* Fill in window class structure with parameters that describe the */
+  /* main window. */
+
+  wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */
+  wc.lpfnWndProc   = (WNDPROC)WndProc; /* Window Procedure */
+  wc.cbClsExtra    = 0;                        /* No per-class extra data. */
+  wc.cbWndExtra    = 0;                        /* No per-window extra data. */
+  wc.hInstance     = hInstance;                /* Owner of this class */
+  wc.hIcon         = LoadIcon(hInstance, "icon_white");
+  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);      /* Cursor */
+  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* Default color */
+  wc.lpszMenuName  = szAppName;                        /* Menu name from .RC */
+  wc.lpszClassName = szAppName;                        /* Name to register as */
+
+  /* Register the window class and return success/failure code. */
+  if (!RegisterClass(&wc)) return FALSE;
+
+  wc.style         = CS_HREDRAW | CS_VREDRAW;
+  wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;
+  wc.cbClsExtra    = 0;
+  wc.cbWndExtra    = DLGWINDOWEXTRA;
+  wc.hInstance     = hInstance;
+  wc.hIcon         = LoadIcon(hInstance, "icon_white");
+  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
+  wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);
+  wc.lpszMenuName  = NULL;
+  wc.lpszClassName = szConsoleName;
+
+  if (!RegisterClass(&wc)) return FALSE;
+  return TRUE;
+}
+
+/* Set by InitInstance, used by EnsureOnScreen */
+int screenHeight, screenWidth;
+
+void
+EnsureOnScreen(int *x, int *y)
+{
+  /* Be sure window at (x,y) is not off screen (or even mostly off screen) */
+  if (*x > screenWidth - 32) *x = 0;
+  if (*y > screenHeight - 32) *y = 0;
+}
+
+BOOL
+InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
+{
+  HWND hwnd; /* Main window handle. */
+  int ibs;
+  WINDOWPLACEMENT wp;
+  char *filepart;
+
+  hInst = hInstance;   /* Store instance handle in our global variable */
+
+  if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {
+    *filepart = NULLCHAR;
+  } else {
+    GetCurrentDirectory(MSG_SIZ, installDir);
+  }
+  InitAppData(lpCmdLine);      /* Get run-time parameters */
+  if (appData.debugMode) {
+    debugFP = fopen("winboard.debug", "w");
+    setbuf(debugFP, NULL);
+  }
+
+  InitBackEnd1();
+
+  /* Create a main window for this application instance. */
+  hwnd = CreateWindow(szAppName, szTitle,
+                     (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),
+                     CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+                     NULL, NULL, hInstance, NULL);
+  hwndMain = hwnd;
+
+  /* If window could not be created, return "failure" */
+  if (!hwnd) {
+    return (FALSE);
+  }
+
+  iconWhite = LoadIcon(hInstance, "icon_white");
+  iconBlack = LoadIcon(hInstance, "icon_black");
+  iconCurrent = iconWhite;
+  InitDrawingColors();
+  screenHeight = GetSystemMetrics(SM_CYSCREEN);
+  screenWidth = GetSystemMetrics(SM_CXSCREEN);
+  for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {
+    /* Compute window size for each board size, and use the largest
+       size that fits on this screen as the default. */
+    InitDrawingSizes((BoardSize)ibs, 0);
+    if (boardSize == (BoardSize)-1 &&
+       winHeight <= screenHeight && winWidth <= screenWidth) {
+      boardSize = (BoardSize)ibs;
+    }
+  }
+  InitDrawingSizes(boardSize, 0);
+  InitMenuChecks();
+  buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);
+
+  InitBackEnd2();
+
+  /* Make the window visible; update its client area; and return "success" */
+  EnsureOnScreen(&boardX, &boardY);
+  wp.length = sizeof(WINDOWPLACEMENT);
+  wp.flags = 0;
+  wp.showCmd = nCmdShow;
+  wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
+  wp.rcNormalPosition.left = boardX;
+  wp.rcNormalPosition.right = boardX + winWidth;
+  wp.rcNormalPosition.top = boardY;
+  wp.rcNormalPosition.bottom = boardY + winHeight;
+  SetWindowPlacement(hwndMain, &wp);
+
+  SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
+              0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+  if (hwndConsole) {
+#if AOT_CONSOLE
+    SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
+                 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+#endif
+    ShowWindow(hwndConsole, nCmdShow);
+  }
+  UpdateWindow(hwnd);
+
+  return TRUE;
+
+}
+
+
+typedef enum {
+  ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, 
+  ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,
+  ArgSettingsFilename
+} ArgType;
+
+typedef struct {
+  char *argName;
+  ArgType argType;
+  /***
+  union {
+    String *pString;       // ArgString
+    int *pInt;             // ArgInt
+    float *pFloat;         // ArgFloat
+    Boolean *pBoolean;     // ArgBoolean
+    COLORREF *pColor;      // ArgColor
+    ColorClass cc;         // ArgAttribs
+    String *pFilename;     // ArgFilename
+    BoardSize *pBoardSize; // ArgBoardSize
+    int whichFont;         // ArgFont
+    DCB *pDCB;             // ArgCommSettings
+    String *pFilename;     // ArgSettingsFilename
+  } argLoc;
+  ***/
+  LPVOID argLoc;
+  BOOL save;
+} ArgDescriptor;
+
+int junk;
+ArgDescriptor argDescriptors[] = {
+  /* positional arguments */
+  { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
+  { "", ArgNone, NULL },
+  /* keyword arguments */
+  { "EngineRoom", ArgBoolean, (LPVOID) &appData.AnalysisWindow, TRUE }, 
+  { "eRoom", ArgTrue, (LPVOID) &appData.AnalysisWindow, FALSE },
+  { "xeRoom", ArgFalse, (LPVOID) &appData.AnalysisWindow, FALSE },
+  { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },
+  { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },
+  { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },
+  { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },
+  { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },
+  { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },
+  { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },
+  { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },
+  { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },
+  { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },
+  { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },
+  { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },
+  { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },
+  { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },
+  { "initString", ArgString, (LPVOID) &appData.initString, FALSE },
+  { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },
+  { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },
+  { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,
+    FALSE },
+  { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,
+    FALSE },
+  { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,
+    FALSE },
+  { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },
+  { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,
+    FALSE },
+  { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },
+  { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },
+  { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },
+  { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
+  { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },
+  { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },
+  { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },
+  { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
+  { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },
+  { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },
+  { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },
+  { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },
+  { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },
+  { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
+  { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },
+  { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
+  { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },
+  /*!!bitmapDirectory?*/
+  { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
+  { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },
+  { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
+  { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },
+  { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },
+  { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },
+  { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },
+  { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },
+  { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },
+  { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },
+  { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },
+  { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },
+  { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
+  { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },
+  { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },
+  { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },
+  { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },
+  { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },
+  { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
+  { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
+  { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
+  { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },
+  { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
+  { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },
+  { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },
+  { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },
+  { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
+  { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },
+  { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },
+  { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },
+  { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },
+  { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
+  { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },
+  { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
+  { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },
+  { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },
+  { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },
+  { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },
+  { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },
+  { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
+  { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },
+  { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
+  { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },
+  { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
+  { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },
+  { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
+  { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },
+  { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },
+  { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },
+  { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
+  { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },
+  { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },
+  { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },
+  { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },
+  { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },
+  { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
+  { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },
+  { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },
+  { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },
+  { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
+  { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },
+  { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },
+  { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },
+  { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
+  { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },
+  { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },
+  { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },
+  { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
+  { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },
+  { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },
+  { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },
+  { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
+  { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },
+  { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },
+  { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },
+  { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
+  { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },
+  { "icc", ArgTrue, (LPVOID) &appData.ICC_feature, FALSE },
+  { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },
+  { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },
+  { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
+  { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },
+  { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },
+  { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },
+  { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
+  { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },
+  { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },
+  { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },
+  { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
+  { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },
+  { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },
+  { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },
+  { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
+  { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },
+  { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, 
+    FALSE }, /* only so that old WinBoard.ini files from betas can be read */
+  { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },
+  { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },
+  { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },
+  { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },
+  { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },
+  { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },
+  { "boardSize", ArgBoardSize, (LPVOID) &boardSize,
+    TRUE }, /* must come after all fonts */
+  { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },
+  { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,
+    FALSE }, /* historical; kept only so old winboard.ini files will parse */
+  { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },
+  { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },
+  { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
+  { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },
+  { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },
+  { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },
+  { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
+  { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },
+  { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },
+  { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },
+  { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
+  { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },
+  { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },
+  { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },
+  { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
+  { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },
+  { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },
+  { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },
+  { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
+  { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },
+  { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },
+  { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },
+  { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
+  { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },
+  { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },
+  { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },
+  { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
+  { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },
+#if 0
+  { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
+  { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },
+#endif
+  { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },
+  { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
+  { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
+  { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },
+  { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },
+  { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },
+  { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
+  { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },
+  { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },
+  { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },
+  { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
+  { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },
+  { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },
+  { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },
+  { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
+  { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },
+  { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },
+  { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },
+  { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
+  { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },
+  { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },
+  { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },
+  { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },
+  { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },
+  { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },
+  { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },
+  { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
+  { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },
+  { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },
+  { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },
+  { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },
+  { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
+  { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },
+  { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },
+  { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},
+  { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},
+  { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
+  { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},
+  { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},
+  { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
+  { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},
+  { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },
+  { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
+  { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },
+  { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },
+  { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },
+  { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },
+  { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },
+  { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },
+  { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },
+  { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },
+  { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
+  { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },
+  { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },
+  { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },
+  { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
+  { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },
+  { "highlightLastMove", ArgBoolean,
+    (LPVOID) &appData.highlightLastMove, TRUE },
+  { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },
+  { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
+  { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },
+  { "highlightDragging", ArgBoolean,
+    (LPVOID) &appData.highlightDragging, TRUE },
+  { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },
+  { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
+  { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },
+  { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },
+  { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },
+  { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
+  { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },
+  { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },
+  { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },
+  { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },
+  { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },
+  { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },
+  { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },
+  { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },
+  { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },
+  { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },
+  { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },
+  { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },
+  { "soundShout", ArgFilename,
+    (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },
+  { "soundSShout", ArgFilename,
+    (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },
+  { "soundChannel1", ArgFilename,
+    (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },
+  { "soundChannel", ArgFilename,
+    (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },
+  { "soundKibitz", ArgFilename,
+    (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },
+  { "soundTell", ArgFilename,
+    (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },
+  { "soundChallenge", ArgFilename,
+    (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },
+  { "soundRequest", ArgFilename,
+    (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },
+  { "soundSeek", ArgFilename,
+    (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },
+  { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },
+  { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },
+  { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },
+  { "soundIcsLoss", ArgFilename, 
+    (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },
+  { "soundIcsDraw", ArgFilename, 
+    (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },
+  { "soundIcsUnfinished", ArgFilename, 
+    (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},
+  { "soundIcsAlarm", ArgFilename, 
+    (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },
+  { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },
+  { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },
+  { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
+  { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },
+  { "reuseChessPrograms", ArgBoolean,
+    (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */
+  { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },
+  { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },
+  { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
+  { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },
+  { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },
+  { "x", ArgInt, (LPVOID) &boardX, TRUE },
+  { "y", ArgInt, (LPVOID) &boardY, TRUE },
+  { "icsX", ArgInt, (LPVOID) &consoleX, TRUE },
+  { "icsY", ArgInt, (LPVOID) &consoleY, TRUE },
+  { "icsW", ArgInt, (LPVOID) &consoleW, TRUE },
+  { "icsH", ArgInt, (LPVOID) &consoleH, TRUE },
+  { "analysisX", ArgInt, (LPVOID) &analysisX, TRUE },
+  { "analysisY", ArgInt, (LPVOID) &analysisY, TRUE },
+  { "analysisW", ArgInt, (LPVOID) &analysisW, TRUE },
+  { "analysisH", ArgInt, (LPVOID) &analysisH, TRUE },
+  { "commentX", ArgInt, (LPVOID) &commentX, TRUE },
+  { "commentY", ArgInt, (LPVOID) &commentY, TRUE },
+  { "commentW", ArgInt, (LPVOID) &commentW, TRUE },
+  { "commentH", ArgInt, (LPVOID) &commentH, TRUE },
+  { "tagsX", ArgInt, (LPVOID) &editTagsX, TRUE },
+  { "tagsY", ArgInt, (LPVOID) &editTagsY, TRUE },
+  { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },
+  { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },
+  { "gameListX", ArgInt, (LPVOID) &gameListX, TRUE },
+  { "gameListY", ArgInt, (LPVOID) &gameListY, TRUE },
+  { "gameListW", ArgInt, (LPVOID) &gameListW, TRUE },
+  { "gameListH", ArgInt, (LPVOID) &gameListH, TRUE },
+  { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
+  { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },
+  { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },
+  { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },
+  { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },
+  { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },
+  { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },
+  { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },
+  { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },
+  { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,
+    TRUE },
+  { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,
+    TRUE },
+  { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },
+  { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },
+  { "variant", ArgString, (LPVOID) &appData.variant, FALSE },
+  { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion,
+    FALSE },
+  { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,
+    FALSE },
+  { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },
+  { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },
+  { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
+  { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },
+#ifdef ZIPPY
+  { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },
+  { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },
+  { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
+  { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },
+  { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },
+  { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },
+  { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
+  { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },
+  { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },
+  { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },
+  { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },
+  { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },
+  { "zippyPassword3", ArgString, (LPVOID) &appData.zippyPassword3, FALSE },
+  { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,
+    FALSE },
+  { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },
+  { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },
+  { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },
+  { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
+  { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },
+  { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },
+  { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,
+    FALSE },
+  { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
+  { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
+  { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },
+  { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },
+  { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },
+  { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },
+  { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },
+  { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
+  { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },
+  { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },
+  { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },
+  { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
+  { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },
+  { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },
+  { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },
+  { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },
+  /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */
+  { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },
+#endif
+  { NULL, ArgNone, NULL, FALSE }
+};
+
+
+/* Kludge for indirection files on command line */
+char* lastIndirectionFilename;
+ArgDescriptor argDescriptorIndirection =
+{ "", ArgSettingsFilename, (LPVOID) NULL, FALSE };
+
+
+VOID
+ExitArgError(char *msg, char *badArg)
+{
+  char buf[MSG_SIZ];
+
+  sprintf(buf, "%s %s", msg, badArg);
+  DisplayFatalError(buf, 0, 2);
+  exit(2);
+}
+
+/* Command line font name parser.  NULL name means do nothing.
+   Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"
+   For backward compatibility, syntax without the colon is also
+   accepted, but font names with digits in them won't work in that case.
+*/
+VOID
+ParseFontName(char *name, MyFontParams *mfp)
+{
+  char *p, *q;
+  if (name == NULL) return;
+  p = name;
+  q = strchr(p, ':');
+  if (q) {
+    if (q - p >= sizeof(mfp->faceName))
+      ExitArgError("Font name too long:", name);
+    memcpy(mfp->faceName, p, q - p);
+    mfp->faceName[q - p] = NULLCHAR;
+    p = q + 1;
+  } else {
+    q = mfp->faceName;
+    while (*p && !isdigit(*p)) {
+      *q++ = *p++;
+      if (q - mfp->faceName >= sizeof(mfp->faceName))
+       ExitArgError("Font name too long:", name);
+    }
+    while (q > mfp->faceName && q[-1] == ' ') q--;
+    *q = NULLCHAR;
+  }
+  if (!*p) ExitArgError("Font point size missing:", name);
+  mfp->pointSize = (float) atof(p);
+  mfp->bold = (strchr(p, 'b') != NULL);
+  mfp->italic = (strchr(p, 'i') != NULL);
+  mfp->underline = (strchr(p, 'u') != NULL);
+  mfp->strikeout = (strchr(p, 's') != NULL);
+}
+
+/* Color name parser.
+   X version accepts X color names, but this one
+   handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */
+COLORREF
+ParseColorName(char *name)
+{
+  int red, green, blue, count;
+  char buf[MSG_SIZ];
+
+  count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);
+  if (count != 3) {
+    count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", 
+      &red, &green, &blue);
+  }
+  if (count != 3) {
+    sprintf(buf, "Can't parse color name %s", name);
+    DisplayError(buf, 0);
+    return RGB(0, 0, 0);
+  }
+  return PALETTERGB(red, green, blue);
+}
+
+
+void ParseAttribs(COLORREF *color, int *effects, char* argValue)
+{
+  char *e = argValue;
+  int eff = 0;
+
+  while (*e) {
+    if (*e == 'b')      eff |= CFE_BOLD;
+    else if (*e == 'i') eff |= CFE_ITALIC;
+    else if (*e == 'u') eff |= CFE_UNDERLINE;
+    else if (*e == 's') eff |= CFE_STRIKEOUT;
+    else if (*e == '#' || isdigit(*e)) break;
+    e++;
+  }
+  *effects = eff;
+  *color   = ParseColorName(e);
+}
+
+
+BoardSize
+ParseBoardSize(char *name)
+{
+  BoardSize bs = SizeTiny;
+  while (sizeInfo[bs].name != NULL) {
+    if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;
+    bs++;
+  }
+  ExitArgError("Unrecognized board size value", name);
+  return bs; /* not reached */
+}
+
+
+char
+StringGet(void *getClosure)
+{
+  char **p = (char **) getClosure;
+  return *((*p)++);
+}
+
+char
+FileGet(void *getClosure)
+{
+  int c;
+  FILE* f = (FILE*) getClosure;
+
+  c = getc(f);
+  if (c == EOF)
+    return NULLCHAR;
+  else
+    return (char) c;
+}
+
+/* Parse settings file named "name". If file found, return the
+   full name in fullname and return TRUE; else return FALSE */
+BOOLEAN
+ParseSettingsFile(char *name, char fullname[MSG_SIZ])
+{
+  char *dummy;
+  FILE *f;
+
+  if (SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy)) {
+    f = fopen(fullname, "r");
+    if (f != NULL) {
+      ParseArgs(FileGet, f);
+      fclose(f);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+VOID
+ParseArgs(GetFunc get, void *cl)
+{
+  char argName[ARG_MAX];
+  char argValue[ARG_MAX];
+  ArgDescriptor *ad;
+  char start;
+  char *q;
+  int i, octval;
+  char ch;
+  int posarg = 0;
+
+  ch = get(cl);
+  for (;;) {
+    while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);
+    if (ch == NULLCHAR) break;
+    if (ch == ';') {
+      /* Comment to end of line */
+      ch = get(cl);
+      while (ch != '\n' && ch != NULLCHAR) ch = get(cl);
+      continue;
+    } else if (ch == '/' || ch == '-') {
+      /* Switch */
+      q = argName;
+      while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&
+            ch != '\n' && ch != '\t') {
+       *q++ = ch;
+       ch = get(cl);
+      }
+      *q = NULLCHAR;
+
+      for (ad = argDescriptors; ad->argName != NULL; ad++)
+       if (strcmp(ad->argName, argName + 1) == 0) break;
+
+      if (ad->argName == NULL)
+       ExitArgError("Unrecognized argument", argName);
+
+    } else if (ch == '@') {
+      /* Indirection file */
+      ad = &argDescriptorIndirection;
+      ch = get(cl);
+    } else {
+      /* Positional argument */
+      ad = &argDescriptors[posarg++];
+      strcpy(argName, ad->argName);
+    }
+
+    if (ad->argType == ArgTrue) {
+      *(Boolean *) ad->argLoc = TRUE;
+      continue;
+    }
+    if (ad->argType == ArgFalse) {
+      *(Boolean *) ad->argLoc = FALSE;
+      continue;
+    }
+
+    while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);
+    if (ch == NULLCHAR || ch == '\n') {
+      ExitArgError("No value provided for argument", argName);
+    }
+    q = argValue;
+    if (ch == '{') {
+      // Quoting with { }.  No characters have to (or can) be escaped.
+      // Thus the string cannot contain a '}' character.
+      start = ch;
+      ch = get(cl);
+      while (start) {
+       switch (ch) {
+       case NULLCHAR:
+         start = NULLCHAR;
+         break;
+         
+       case '}':
+         ch = get(cl);
+         start = NULLCHAR;
+         break;
+
+       default:
+         *q++ = ch;
+         ch = get(cl);
+         break;
+       }
+      }          
+    } else if (ch == '\'' || ch == '"') {
+      // Quoting with ' ' or " ", with \ as escape character.
+      // Inconvenient for long strings that may contain Windows filenames.
+      start = ch;
+      ch = get(cl);
+      while (start) {
+       switch (ch) {
+       case NULLCHAR:
+         start = NULLCHAR;
+         break;
+
+       default:
+        not_special:
+         *q++ = ch;
+         ch = get(cl);
+         break;
+
+       case '\'':
+       case '\"':
+         if (ch == start) {
+           ch = get(cl);
+           start = NULLCHAR;
+           break;
+         } else {
+           goto not_special;
+         }
+
+       case '\\':
+          if (ad->argType == ArgFilename
+             || ad->argType == ArgSettingsFilename) {
+             goto not_special;
+         }
+         ch = get(cl);
+         switch (ch) {
+         case NULLCHAR:
+           ExitArgError("Incomplete \\ escape in value for", argName);
+           break;
+         case 'n':
+           *q++ = '\n';
+           ch = get(cl);
+           break;
+         case 'r':
+           *q++ = '\r';
+           ch = get(cl);
+           break;
+         case 't':
+           *q++ = '\t';
+           ch = get(cl);
+           break;
+         case 'b':
+           *q++ = '\b';
+           ch = get(cl);
+           break;
+         case 'f':
+           *q++ = '\f';
+           ch = get(cl);
+           break;
+         default:
+           octval = 0;
+           for (i = 0; i < 3; i++) {
+             if (ch >= '0' && ch <= '7') {
+               octval = octval*8 + (ch - '0');
+               ch = get(cl);
+             } else {
+               break;
+             }
+           }
+           if (i > 0) {
+             *q++ = (char) octval;
+           } else {
+             *q++ = ch;
+             ch = get(cl);
+           }
+           break;
+         }
+         break;
+       }
+      }
+    } else {
+      while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {
+       *q++ = ch;
+       ch = get(cl);
+      }
+    }
+    *q = NULLCHAR;
+
+    switch (ad->argType) {
+    case ArgInt:
+      *(int *) ad->argLoc = atoi(argValue);
+      break;
+
+    case ArgFloat:
+      *(float *) ad->argLoc = (float) atof(argValue);
+      break;
+
+    case ArgString:
+    case ArgFilename:
+      *(char **) ad->argLoc = strdup(argValue);
+      break;
+
+    case ArgSettingsFilename:
+      {
+       char fullname[MSG_SIZ];
+       if (ParseSettingsFile(argValue, fullname)) {
+         if (ad->argLoc != NULL) {
+           *(char **) ad->argLoc = strdup(fullname);
+         }
+       } else {
+         if (ad->argLoc != NULL) {
+         } else {
+           ExitArgError("Failed to open indirection file", argValue);
+         }
+       }
+      }
+      break;
+
+    case ArgBoolean:
+      switch (argValue[0]) {
+      case 't':
+      case 'T':
+       *(Boolean *) ad->argLoc = TRUE;
+       break;
+      case 'f':
+      case 'F':
+       *(Boolean *) ad->argLoc = FALSE;
+       break;
+      default:
+       ExitArgError("Unrecognized boolean argument value", argValue);
+       break;
+      }
+      break;
+
+    case ArgColor:
+      *(COLORREF *)ad->argLoc = ParseColorName(argValue);
+      break;
+
+    case ArgAttribs: {
+      ColorClass cc = (ColorClass)ad->argLoc;
+      ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);
+      }
+      break;
+      
+    case ArgBoardSize:
+      *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);
+      break;
+
+    case ArgFont:
+      ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);
+      break;
+
+    case ArgCommSettings:
+      ParseCommSettings(argValue, &dcb);
+      break;
+
+    case ArgNone:
+      ExitArgError("Unrecognized argument", argValue);
+      break;
+    }
+  }
+}
+
+VOID
+LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
+{
+  HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
+  lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);
+  DeleteDC(hdc);
+  lf->lfWidth = 0;
+  lf->lfEscapement = 0;
+  lf->lfOrientation = 0;
+  lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;
+  lf->lfItalic = mfp->italic;
+  lf->lfUnderline = mfp->underline;
+  lf->lfStrikeOut = mfp->strikeout;
+  lf->lfCharSet = DEFAULT_CHARSET;
+  lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
+  lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+  lf->lfQuality = DEFAULT_QUALITY;
+  lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
+  strcpy(lf->lfFaceName, mfp->faceName);
+}
+
+VOID
+CreateFontInMF(MyFont *mf)
+{
+  LFfromMFP(&mf->lf, &mf->mfp);
+  if (mf->hf) DeleteObject(mf->hf);
+  mf->hf = CreateFontIndirect(&mf->lf);
+}
+
+VOID
+SetDefaultTextAttribs()
+{
+  ColorClass cc;
+  for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
+    ParseAttribs(&textAttribs[cc].color, 
+                &textAttribs[cc].effects, 
+                defaultTextAttribs[cc]);
+  }
+}
+
+VOID
+SetDefaultSounds()
+{
+  ColorClass cc;
+  SoundClass sc;
+  for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
+    textAttribs[cc].sound.name = strdup("");
+    textAttribs[cc].sound.data = NULL;
+  }
+  for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
+    sounds[sc].name = strdup("");
+    sounds[sc].data = NULL;
+  }
+  sounds[(int)SoundBell].name = strdup(SOUND_BELL);
+}
+
+VOID
+LoadAllSounds()
+{
+  ColorClass cc;
+  SoundClass sc;
+  for (cc = (ColorClass)0; cc < NColorClasses; cc++) {
+    MyLoadSound(&textAttribs[cc].sound);
+  }
+  for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {
+    MyLoadSound(&sounds[sc]);
+  }
+}
+
+VOID
+InitAppData(LPSTR lpCmdLine)
+{
+  int i, j;
+  char buf[ARG_MAX], currDir[MSG_SIZ];
+  char *dummy, *p;
+
+  programName = szAppName;
+
+  /* Initialize to defaults */
+  lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);
+  darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);
+  whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);
+  blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);
+  highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);
+  premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);
+  consoleBackgroundColor = ParseColorName(COLOR_BKGD);
+  SetDefaultTextAttribs();
+  SetDefaultSounds();
+  appData.movesPerSession = MOVES_PER_SESSION;
+  appData.initString = INIT_STRING;
+  appData.secondInitString = INIT_STRING;
+  appData.firstComputerString = COMPUTER_STRING;
+  appData.secondComputerString = COMPUTER_STRING;
+  appData.firstChessProgram = FIRST_CHESS_PROGRAM;
+  appData.secondChessProgram = SECOND_CHESS_PROGRAM;
+  appData.firstPlaysBlack = FALSE;
+  appData.noChessProgram = FALSE;
+  chessProgram = FALSE;
+  appData.firstHost = FIRST_HOST;
+  appData.secondHost = SECOND_HOST;
+  appData.firstDirectory = FIRST_DIRECTORY;
+  appData.secondDirectory = SECOND_DIRECTORY;
+  appData.bitmapDirectory = "";
+  appData.remoteShell = REMOTE_SHELL;
+  appData.remoteUser = "";
+  appData.timeDelay = TIME_DELAY;
+  appData.timeControl = TIME_CONTROL;
+  appData.timeIncrement = TIME_INCREMENT;
+  appData.icsActive = FALSE;
+  appData.icsHost = "";
+  appData.icsPort = ICS_PORT;
+  appData.icsCommPort = ICS_COMM_PORT;
+  appData.icsLogon = ICS_LOGON;
+  appData.icsHelper = "";
+  appData.useTelnet = FALSE;
+  appData.telnetProgram = TELNET_PROGRAM;
+  appData.gateway = "";
+  appData.loadGameFile = "";
+  appData.loadGameIndex = 0;
+  appData.saveGameFile = "";
+  appData.autoSaveGames = FALSE;
+  appData.loadPositionFile = "";
+  appData.loadPositionIndex = 1;
+  appData.savePositionFile = "";
+  appData.matchMode = FALSE;
+  appData.matchGames = 0;
+  appData.monoMode = FALSE;
+  appData.debugMode = FALSE;
+  appData.clockMode = TRUE;
+  boardSize = (BoardSize) -1; /* determine by screen size */
+  appData.Iconic = FALSE; /*unused*/
+  appData.searchTime = "";
+  appData.searchDepth = 0;
+  appData.showCoords = FALSE;
+  appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/
+  appData.autoCallFlag = FALSE;
+  appData.flipView = FALSE;
+  appData.autoFlipView = TRUE;
+  appData.cmailGameName = "";
+  appData.alwaysPromoteToQueen = FALSE;
+  appData.oldSaveStyle = FALSE;
+  appData.quietPlay = FALSE;
+  appData.showThinking = FALSE;
+  appData.ButtonSendOutPutToICS = TRUE;
+  appData.SendOutPutToICS = 1; /* default whisper */
+  appData.icsAnalyze = FALSE;
+  appData.icsEngineKillPV = FALSE;
+  appData.smartQueue = TRUE;   /* smart queue with IcsAnalyzeOutPut */
+  appData.icsEngineWhisper = FALSE;
+  appData.icsEngineKibitz = FALSE;
+  appData.icsEngineTell = FALSE;
+  appData.icsAnalyzeWindow = FALSE;
+  appData.icsEngineNone = TRUE; /* don't send */
+  appData.icsAnalyzeOutPut = 4; /* don't send */
+  appData.icsKillPVs = 9;              /* a recommend value for most programs */
+  appData.windowMove = TRUE;   /* Drop display fail low/high moves at ICS */
+  appData.icsSmartQueue = 0;   /* 0 = standard 1 = blitz */
+  appData.icsShowBook = FALSE; /* show engine book */
+  appData.userVersion = FALSE; /* programmer version false */
+  appData.icsWBprotoAgr = FALSE;
+  appData.icsWBprotoNorm = TRUE;
+  appData.icsSmartQueueStd = TRUE;
+  appData.icsSmartQueueBlitz = FALSE;
+  appData.engineStatLine = TRUE; /* default send "." with engine room */
+  appData.engineTourneyMode = FALSE; /* default no tourney mode */
+  appData.zippyDraw = TRUE;
+  appData.ponderNextMove = TRUE;
+  appData.periodicUpdates = TRUE;
+  appData.popupExitMessage = TRUE;
+  appData.popupMoveErrors = FALSE;
+  appData.autoObserve = FALSE;
+  appData.autoComment = FALSE;
+  appData.animate = TRUE;
+  appData.animSpeed = 10;
+  appData.animateDragging = TRUE;
+  appData.highlightLastMove = TRUE;
+  appData.getMoveList = TRUE;
+  appData.testLegality = TRUE;
+  appData.premove = TRUE;
+  appData.premoveWhite = FALSE;
+  appData.premoveWhiteText = "";
+  appData.premoveBlack = FALSE;
+  appData.premoveBlackText = "";
+  appData.icsAlarm = TRUE;
+  appData.icsAlarmTime = 5000;
+  appData.autoRaiseBoard = TRUE;
+  appData.localLineEditing = TRUE;
+  appData.colorize = TRUE;
+  appData.reuseFirst = TRUE;
+  appData.reuseSecond = TRUE;
+  appData.blindfold = FALSE;
+  dcb.DCBlength = sizeof(DCB);
+  dcb.BaudRate = 9600;
+  dcb.fBinary = TRUE;
+  dcb.fParity = FALSE;
+  dcb.fOutxCtsFlow = FALSE;
+  dcb.fOutxDsrFlow = FALSE;
+  dcb.fDtrControl = DTR_CONTROL_ENABLE;
+  dcb.fDsrSensitivity = FALSE;
+  dcb.fTXContinueOnXoff = TRUE;
+  dcb.fOutX = FALSE;
+  dcb.fInX = FALSE;
+  dcb.fNull = FALSE;
+  dcb.fRtsControl = RTS_CONTROL_ENABLE;
+  dcb.fAbortOnError = FALSE;
+  dcb.wReserved = 0;
+  dcb.ByteSize = 7;
+  dcb.Parity = SPACEPARITY;
+  dcb.StopBits = ONESTOPBIT;
+  settingsFileName = SETTINGS_FILE;
+  saveSettingsOnExit = TRUE;
+  boardX = CW_USEDEFAULT;
+  boardY = CW_USEDEFAULT;
+  consoleX = CW_USEDEFAULT; 
+  consoleY = CW_USEDEFAULT; 
+  consoleW = CW_USEDEFAULT;
+  consoleH = CW_USEDEFAULT;
+  analysisX = CW_USEDEFAULT; 
+  analysisY = CW_USEDEFAULT;
+  analysisW = 589;
+  analysisH = 330;
+  commentX = CW_USEDEFAULT; 
+  commentY = CW_USEDEFAULT; 
+  commentW = CW_USEDEFAULT;
+  commentH = CW_USEDEFAULT;
+  editTagsX = CW_USEDEFAULT; 
+  editTagsY = CW_USEDEFAULT; 
+  editTagsW = CW_USEDEFAULT;
+  editTagsH = CW_USEDEFAULT;
+  gameListX = CW_USEDEFAULT; 
+  gameListY = CW_USEDEFAULT; 
+  gameListW = CW_USEDEFAULT;
+  gameListH = CW_USEDEFAULT;
+  icsTextMenuString = ICS_TEXT_MENU_DEFAULT;
+  icsNames = ICS_NAMES;
+  firstChessProgramNames = FCP_NAMES;
+  secondChessProgramNames = SCP_NAMES;
+  appData.initialMode = "";
+  appData.variant = "normal";
+  appData.firstProtocolVersion = PROTOVER;
+  appData.secondProtocolVersion = PROTOVER;
+  appData.showButtonBar = TRUE;
+#ifdef ZIPPY
+  appData.zippyTalk = ZIPPY_TALK;
+  appData.zippyPlay = ZIPPY_PLAY;
+  appData.zippyLines = ZIPPY_LINES;
+  appData.zippyPinhead = ZIPPY_PINHEAD;
+  appData.zippyPassword = ZIPPY_PASSWORD;
+  appData.zippyPassword2 = ZIPPY_PASSWORD2;
+  appData.zippyPassword3 = ZIPPY_PASSWORD3;
+  appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;
+  appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;
+  appData.zippyUseI = ZIPPY_USE_I;
+  appData.zippyBughouse = ZIPPY_BUGHOUSE;
+  appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;
+  appData.zippyGameEnd = ZIPPY_GAME_END;
+  appData.zippyGameStart = ZIPPY_GAME_START;
+  appData.zippyAdjourn = ZIPPY_ADJOURN;
+  appData.zippyAbort = ZIPPY_ABORT;
+  appData.zippyVariants = ZIPPY_VARIANTS;
+  appData.zippyMaxGames = ZIPPY_MAX_GAMES;
+  appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;
+#endif
+
+  /* Point font array elements to structures and
+     parse default font names */
+  for (i=0; i<NUM_FONTS; i++) {
+    for (j=0; j<NUM_SIZES; j++) {
+      font[j][i] = &fontRec[j][i];
+      ParseFontName(font[j][i]->def, &font[j][i]->mfp);
+    }
+  }
+  
+  /* Parse default settings file if any */
+  if (ParseSettingsFile(settingsFileName, buf)) {
+    settingsFileName = strdup(buf);
+  }
+
+  /* Parse command line */
+  ParseArgs(StringGet, &lpCmdLine);
+
+  /* Propagate options that affect others */
+  if (appData.matchMode || appData.matchGames) chessProgram = TRUE;
+  if (appData.icsActive || appData.noChessProgram) {
+     chessProgram = FALSE;  /* not local chess program mode */
+  }
+
+  /* Open startup dialog if needed */
+  if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||
+      (appData.icsActive && *appData.icsHost == NULLCHAR) ||
+      (chessProgram && (*appData.firstChessProgram == NULLCHAR ||
+                        *appData.secondChessProgram == NULLCHAR))) {
+    FARPROC lpProc;
+    
+    lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);
+    DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);
+    FreeProcInstance(lpProc);
+  }
+
+  /* Make sure save files land in the right (?) directory */
+  if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {
+    appData.saveGameFile = strdup(buf);
+  }
+  if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {
+    appData.savePositionFile = strdup(buf);
+  }
+
+  /* Finish initialization for fonts and sounds */
+  for (i=0; i<NUM_FONTS; i++) {
+    for (j=0; j<NUM_SIZES; j++) {
+      CreateFontInMF(font[j][i]);
+    }
+  }
+  /* xboard, and older WinBoards, controlled the move sound with the
+     appData.ringBellAfterMoves option.  In the current WinBoard, we
+     always turn the option on (so that the backend will call us),
+     then let the user turn the sound off by setting it to silence if
+     desired.  To accommodate old winboard.ini files saved by old
+     versions of WinBoard, we also turn off the sound if the option
+     was initially set to false. */
+  if (!appData.ringBellAfterMoves) {
+    sounds[(int)SoundMove].name = strdup("");
+    appData.ringBellAfterMoves = TRUE;
+  }
+  GetCurrentDirectory(MSG_SIZ, currDir);
+  SetCurrentDirectory(installDir);
+  LoadAllSounds();
+  SetCurrentDirectory(currDir);
+
+  p = icsTextMenuString;
+  if (p[0] == '@') {
+    FILE* f = fopen(p + 1, "r");
+    if (f == NULL) {
+      DisplayFatalError(p + 1, errno, 2);
+      return;
+    }
+    i = fread(buf, 1, sizeof(buf)-1, f);
+    fclose(f);
+    buf[i] = NULLCHAR;
+    p = buf;
+  }
+  ParseIcsTextMenu(strdup(p));
+}
+
+
+VOID
+InitMenuChecks()
+{
+  HMENU hmenu = GetMenu(hwndMain);
+
+  (void) EnableMenuItem(hmenu, IDM_CommPort,
+                       MF_BYCOMMAND|((appData.icsActive &&
+                                      *appData.icsCommPort != NULLCHAR) ?
+                                     MF_ENABLED : MF_GRAYED));
+  (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,
+                      MF_BYCOMMAND|(saveSettingsOnExit ?
+                                    MF_CHECKED : MF_UNCHECKED));
+}
+
+
+VOID
+SaveSettings(char* name)
+{
+  FILE *f;
+  ArgDescriptor *ad;
+  WINDOWPLACEMENT wp;
+  char dir[MSG_SIZ];
+
+  if (!hwndMain) return;
+
+  GetCurrentDirectory(MSG_SIZ, dir);
+  SetCurrentDirectory(installDir);
+  f = fopen(name, "w");
+  SetCurrentDirectory(dir);
+  if (f == NULL) {
+    DisplayError(name, errno);
+    return;
+  }
+  fprintf(f, ";\n");
+  fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);
+  fprintf(f, ";\n");
+  fprintf(f, "; You can edit the values of options that are already set in this file,\n");
+  fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");
+  fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");
+  fprintf(f, ";\n");
+
+  wp.length = sizeof(WINDOWPLACEMENT);
+  GetWindowPlacement(hwndMain, &wp);
+  boardX = wp.rcNormalPosition.left;
+  boardY = wp.rcNormalPosition.top;
+
+  if (hwndConsole) {
+    GetWindowPlacement(hwndConsole, &wp);
+    consoleX = wp.rcNormalPosition.left;
+    consoleY = wp.rcNormalPosition.top;
+    consoleW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    consoleH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
+  if (analysisDialog) {
+    GetWindowPlacement(analysisDialog, &wp);
+    analysisX = wp.rcNormalPosition.left;
+    analysisY = wp.rcNormalPosition.top;
+    analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
+  if (commentDialog) {
+    GetWindowPlacement(commentDialog, &wp);
+    commentX = wp.rcNormalPosition.left;
+    commentY = wp.rcNormalPosition.top;
+    commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
+  if (editTagsDialog) {
+    GetWindowPlacement(editTagsDialog, &wp);
+    editTagsX = wp.rcNormalPosition.left;
+    editTagsY = wp.rcNormalPosition.top;
+    editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
+  if (gameListDialog) {
+    GetWindowPlacement(gameListDialog, &wp);
+    gameListX = wp.rcNormalPosition.left;
+    gameListY = wp.rcNormalPosition.top;
+    gameListW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;
+    gameListH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
+  }
+
+  for (ad = argDescriptors; ad->argName != NULL; ad++) {
+    if (!ad->save) continue;
+    switch (ad->argType) {
+    case ArgString:
+      {
+       char *p = *(char **)ad->argLoc;
+       if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {
+         /* Quote multiline values or \-containing values
+            with { } if possible */
+         fprintf(f, "/%s={%s}\n", ad->argName, p);
+       } else {
+         /* Else quote with " " */
+         fprintf(f, "/%s=\"", ad->argName);
+         while (*p) {
+           if (*p == '\n') fprintf(f, "\n");
+           else if (*p == '\r') fprintf(f, "\\r");
+           else if (*p == '\t') fprintf(f, "\\t");
+           else if (*p == '\b') fprintf(f, "\\b");
+           else if (*p == '\f') fprintf(f, "\\f");
+           else if (*p < ' ') fprintf(f, "\\%03o", *p);
+           else if (*p == '\"') fprintf(f, "\\\"");
+           else if (*p == '\\') fprintf(f, "\\\\");
+           else putc(*p, f);
+           p++;
+         }
+         fprintf(f, "\"\n");
+       }
+      }
+      break;
+    case ArgInt:
+      fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);
+      break;
+    case ArgFloat:
+      fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);
+      break;
+    case ArgBoolean:
+      fprintf(f, "/%s=%s\n", ad->argName, 
+       (*(Boolean *)ad->argLoc) ? "true" : "false");
+      break;
+    case ArgTrue:
+      if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
+      break;
+    case ArgFalse:
+      if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);
+      break;
+    case ArgColor:
+      {
+       COLORREF color = *(COLORREF *)ad->argLoc;
+       fprintf(f, "/%s=#%02x%02x%02x\n", ad->argName, 
+         color&0xff, (color>>8)&0xff, (color>>16)&0xff);
+      }
+      break;
+    case ArgAttribs:
+      {
+       MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];
+       fprintf(f, "/%s=\"%s%s%s%s%s#%02x%02x%02x\"\n", ad->argName,
+          (ta->effects & CFE_BOLD) ? "b" : "",
+          (ta->effects & CFE_ITALIC) ? "i" : "",
+          (ta->effects & CFE_UNDERLINE) ? "u" : "",
+          (ta->effects & CFE_STRIKEOUT) ? "s" : "",
+          (ta->effects) ? " " : "",
+         ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);
+      }
+      break;
+    case ArgFilename:
+      if (strchr(*(char **)ad->argLoc, '\"')) {
+       fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);
+      } else {
+       fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);
+      }
+      break;
+    case ArgBoardSize:
+      fprintf(f, "/%s=%s\n", ad->argName,
+             sizeInfo[*(BoardSize *)ad->argLoc].name);
+      break;
+    case ArgFont:
+      {
+        int bs;
+       for (bs=0; bs<NUM_SIZES; bs++) {
+         MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;
+          fprintf(f, "/size=%s ", sizeInfo[bs].name);
+         fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",
+           ad->argName, mfp->faceName, mfp->pointSize,
+            mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",
+           mfp->bold ? "b" : "",
+           mfp->italic ? "i" : "",
+           mfp->underline ? "u" : "",
+           mfp->strikeout ? "s" : "");
+       }
+      }
+      break;
+    case ArgCommSettings:
+      PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);
+    }
+  }
+  fclose(f);
+}
+
+
+
+/*---------------------------------------------------------------------------*\
+ *
+ * GDI board drawing routines
+ *
+\*---------------------------------------------------------------------------*/
+
+HBITMAP
+DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
+{
+  char name[128];
+
+  sprintf(name, "%s%d%s", piece, squareSize, suffix);
+  if (gameInfo.event &&
+      strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&
+      strcmp(name, "k80s") == 0) {
+    strcpy(name, "tim");
+  }
+  return LoadBitmap(hinst, name);
+}
+
+
+/* Insert a color into the program's logical palette
+   structure.  This code assumes the given color is
+   the result of the RGB or PALETTERGB macro, and it
+   knows how those macros work (which is documented).
+*/
+VOID
+InsertInPalette(COLORREF color)
+{
+  LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);
+
+  if (pLogPal->palNumEntries++ >= PALETTESIZE) {
+    DisplayFatalError("Too many colors", 0, 1);
+    pLogPal->palNumEntries--;
+    return;
+  }
+
+  pe->peFlags = (char) 0;
+  pe->peRed = (char) (0xFF & color);
+  pe->peGreen = (char) (0xFF & (color >> 8));
+  pe->peBlue = (char) (0xFF & (color >> 16));
+  return;
+}
+
+
+VOID
+InitDrawingColors()
+{
+  if (pLogPal == NULL) {
+    /* Allocate enough memory for a logical palette with
+     * PALETTESIZE entries and set the size and version fields
+     * of the logical palette structure.
+     */
+    pLogPal = (NPLOGPALETTE)
+      LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +
+                             (sizeof(PALETTEENTRY) * (PALETTESIZE))));
+    pLogPal->palVersion    = 0x300;
+  }
+  pLogPal->palNumEntries = 0;
+
+  InsertInPalette(lightSquareColor);
+  InsertInPalette(darkSquareColor);
+  InsertInPalette(whitePieceColor);
+  InsertInPalette(blackPieceColor);
+  InsertInPalette(highlightSquareColor);
+  InsertInPalette(premoveHighlightColor);
+
+  /*  create a logical color palette according the information
+   *  in the LOGPALETTE structure.
+   */
+  hPal = CreatePalette((LPLOGPALETTE) pLogPal);
+
+  lightSquareBrush = CreateSolidBrush(lightSquareColor);
+  darkSquareBrush = CreateSolidBrush(darkSquareColor);
+  whitePieceBrush = CreateSolidBrush(whitePieceColor);
+  blackPieceBrush = CreateSolidBrush(blackPieceColor);
+  iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));
+}
+
+
+int
+BoardWidth(int boardSize)
+{
+  return (BOARD_SIZE + 1) * sizeInfo[boardSize].lineGap +
+         BOARD_SIZE * sizeInfo[boardSize].squareSize;
+}
+
+/* Respond to board resize by dragging edge */
+VOID
+ResizeBoard(int newSizeX, int newSizeY, int flags)
+{
+  BoardSize newSize = NUM_SIZES - 1;
+  static int recurse = 0;
+  if (IsIconic(hwndMain)) return;
+  if (recurse > 0) return;
+  recurse++;
+  while (newSize > 0 &&
+        (newSizeX < sizeInfo[newSize].cliWidth ||
+         newSizeY < sizeInfo[newSize].cliHeight)) {
+    newSize--;
+  } 
+  boardSize = newSize;
+  InitDrawingSizes(boardSize, flags);
+  recurse--;
+}
+
+
+
+VOID
+InitDrawingSizes(BoardSize boardSize, int flags)
+{
+  int i, boardWidth;
+  ChessSquare piece;
+  static int oldBoardSize = -1, oldTinyLayout = 0;
+  HDC hdc;
+  SIZE clockSize, messageSize;
+  HFONT oldFont;
+  char buf[MSG_SIZ];
+  char *str;
+  HMENU hmenu = GetMenu(hwndMain);
+  RECT crect, wrect;
+  int offby;
+  LOGBRUSH logbrush;
+
+  tinyLayout = sizeInfo[boardSize].tinyLayout;
+  smallLayout = sizeInfo[boardSize].smallLayout;
+  squareSize = sizeInfo[boardSize].squareSize;
+  lineGap = sizeInfo[boardSize].lineGap;
+
+  if (tinyLayout != oldTinyLayout) {
+    long style = GetWindowLong(hwndMain, GWL_STYLE);
+    if (tinyLayout) {
+      style &= ~WS_SYSMENU;
+      InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,
+                "&Minimize\tCtrl+F4");
+    } else {
+      style |= WS_SYSMENU;
+      RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);
+    }
+    SetWindowLong(hwndMain, GWL_STYLE, style);
+
+    for (i=0; menuBarText[tinyLayout][i]; i++) {
+      ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, 
+       (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);
+    }
+    DrawMenuBar(hwndMain);
+  }
+
+  boardWidth = BoardWidth(boardSize);
+
+  /* Get text area sizes */
+  hdc = GetDC(hwndMain);
+  if (appData.clockMode) {
+    sprintf(buf, "White: %s", TimeString(23*60*60*1000L));
+  } else {
+    sprintf(buf, "White");
+  }
+  oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
+  GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);
+  SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
+  str = "We only care about the height here";
+  GetTextExtentPoint(hdc, str, strlen(str), &messageSize);
+  SelectObject(hdc, oldFont);
+  ReleaseDC(hwndMain, hdc);
+
+  /* Compute where everything goes */
+  whiteRect.left = OUTER_MARGIN;
+  whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;
+  whiteRect.top = OUTER_MARGIN;
+  whiteRect.bottom = whiteRect.top + clockSize.cy;
+
+  blackRect.left = whiteRect.right + INNER_MARGIN;
+  blackRect.right = blackRect.left + boardWidth/2 - 1;
+  blackRect.top = whiteRect.top;
+  blackRect.bottom = whiteRect.bottom;
+
+  messageRect.left = whiteRect.left + MESSAGE_LINE_LEFTMARGIN;
+  if (appData.showButtonBar) {
+    messageRect.right = blackRect.right
+      - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;
+  } else {
+    messageRect.right = blackRect.right;
+  }
+  messageRect.top = whiteRect.bottom + INNER_MARGIN;
+  messageRect.bottom = messageRect.top + messageSize.cy;
+
+  boardRect.left = whiteRect.left;
+  boardRect.right = boardRect.left + boardWidth;
+  boardRect.top = messageRect.bottom + INNER_MARGIN;
+  boardRect.bottom = boardRect.top + boardWidth;
+
+  sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;
+  sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;
+  winWidth = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;
+  winHeight = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +
+    GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;
+  GetWindowRect(hwndMain, &wrect);
+  SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
+              SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
+  /* compensate if menu bar wrapped */
+  GetClientRect(hwndMain, &crect);
+  offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;
+  winHeight += offby;
+  switch (flags) {
+  case WMSZ_TOPLEFT:
+    SetWindowPos(hwndMain, NULL, 
+                 wrect.right - winWidth, wrect.bottom - winHeight, 
+                 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
+    break;
+
+  case WMSZ_TOPRIGHT:
+  case WMSZ_TOP:
+    SetWindowPos(hwndMain, NULL, 
+                 wrect.left, wrect.bottom - winHeight, 
+                 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
+    break;
+
+  case WMSZ_BOTTOMLEFT:
+  case WMSZ_LEFT:
+    SetWindowPos(hwndMain, NULL, 
+                 wrect.right - winWidth, wrect.top, 
+                 winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);
+    break;
+
+  case WMSZ_BOTTOMRIGHT:
+  case WMSZ_BOTTOM:
+  case WMSZ_RIGHT:
+  default:
+    SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,
+               SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);
+    break;
+  }
+
+  hwndPause = NULL;
+  for (i = 0; i < N_BUTTONS; i++) {
+    if (buttonDesc[i].hwnd != NULL) {
+      DestroyWindow(buttonDesc[i].hwnd);
+      buttonDesc[i].hwnd = NULL;
+    }
+    if (appData.showButtonBar) {
+      buttonDesc[i].hwnd =
+       CreateWindow("BUTTON", buttonDesc[i].label,
+                    WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON ,
+                    boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),
+                    messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,
+                    (HMENU) buttonDesc[i].id,
+                    (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);
+      if (tinyLayout) {
+       SendMessage(buttonDesc[i].hwnd, WM_SETFONT, 
+                   (WPARAM)font[boardSize][MESSAGE_FONT]->hf,
+                   MAKELPARAM(FALSE, 0));
+      }
+      if (buttonDesc[i].id == IDM_Pause)
+       hwndPause = buttonDesc[i].hwnd;
+      buttonDesc[i].wndproc = (WNDPROC)
+       SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);
+    }
+  }
+  if (gridPen != NULL) DeleteObject(gridPen);
+  if (highlightPen != NULL) DeleteObject(highlightPen);
+  if (premovePen != NULL) DeleteObject(premovePen);
+  if (lineGap != 0) {
+    logbrush.lbStyle = BS_SOLID;
+    logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */
+    gridPen =
+      ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
+                   lineGap, &logbrush, 0, NULL);
+    logbrush.lbColor = highlightSquareColor;
+    highlightPen =
+      ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
+                   lineGap, &logbrush, 0, NULL);
+
+    logbrush.lbColor = premoveHighlightColor; 
+    premovePen =
+      ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,
+                   lineGap, &logbrush, 0, NULL);
+
+    for (i = 0; i < BOARD_SIZE + 1; i++) {
+      gridEndpoints[i*2].x = boardRect.left + lineGap / 2;
+      gridEndpoints[i*2 + BOARD_SIZE*2 + 2].y = boardRect.top + lineGap / 2;
+      gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =
+       boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));
+      gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +
+       BOARD_SIZE * (squareSize + lineGap);
+      gridEndpoints[i*2 + BOARD_SIZE*2 + 2].x =
+       gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].x = boardRect.left +
+       lineGap / 2 + (i * (squareSize + lineGap));
+      gridEndpoints[i*2 + 1 + BOARD_SIZE*2 + 2].y =
+       boardRect.top + BOARD_SIZE * (squareSize + lineGap);
+      gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;
+    }
+  }
+
+  if (boardSize == oldBoardSize) return;
+  oldBoardSize = boardSize;
+  oldTinyLayout = tinyLayout;
+
+  /* Load piece bitmaps for this board size */
+  for (i=0; i<=2; i++) {
+    for (piece = WhitePawn;
+        (int) piece <= (int) WhiteKing;
+        piece = (ChessSquare) ((int) piece + 1)) {
+      if (pieceBitmap[i][piece] != NULL)
+       DeleteObject(pieceBitmap[i][piece]);
+    }
+  }
+
+  pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");
+  pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");
+  pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");
+  pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");
+  pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");
+  pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");
+  pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");
+  pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");
+  pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");
+  pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");
+  pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");
+  pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");
+  pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");
+  pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");
+  pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");
+  pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");
+  pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");
+  pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");
+
+}
+
+HBITMAP
+PieceBitmap(ChessSquare p, int kind)
+{
+  if ((int) p >= (int) BlackPawn)
+    p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);
+
+  return pieceBitmap[kind][(int) p];
+}
+
+/***************************************************************/
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+/*
+#define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))
+#define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))
+*/
+
+VOID
+SquareToPos(int row, int column, int * x, int * y)
+{
+  if (flipView) {
+    *x = boardRect.left + lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
+    *y = boardRect.top + lineGap + row * (squareSize + lineGap);
+  } else {
+    *x = boardRect.left + lineGap + column * (squareSize + lineGap);
+    *y = boardRect.top + lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
+  }
+}
+
+VOID
+DrawCoordsOnDC(HDC hdc)
+{
+  static char files[16] = {'1','2','3','4','5','6','7','8','8','7','6','5','4','3','2','1'};
+  static char ranks[16] = {'h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h'};
+  char str[2] = { NULLCHAR, NULLCHAR };
+  int oldMode, oldAlign, x, y, start, i;
+  HFONT oldFont;
+  HBRUSH oldBrush;
+
+  if (!appData.showCoords)
+    return;
+
+  start = flipView ? 0 : 8;
+
+  oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
+  oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));
+  oldAlign = GetTextAlign(hdc);
+  oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);
+
+  y = boardRect.top + lineGap;
+  x = boardRect.left + lineGap;
+
+  SetTextAlign(hdc, TA_LEFT|TA_TOP);
+  for (i = 0; i < 8; i++) {
+    str[0] = files[start + i];
+    ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);
+    y += squareSize + lineGap;
+  }
+
+  SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
+  for (i = 0; i < 8; i++) {
+    str[0] = ranks[start + i];
+    ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);
+    x += squareSize + lineGap;
+  }    
+
+  SelectObject(hdc, oldBrush);
+  SetBkMode(hdc, oldMode);
+  SetTextAlign(hdc, oldAlign);
+  SelectObject(hdc, oldFont);
+}
+
+VOID
+DrawGridOnDC(HDC hdc)
+{
+  HPEN oldPen;
+  if (lineGap != 0) {
+    oldPen = SelectObject(hdc, gridPen);
+    PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_SIZE*2 + 2);
+    SelectObject(hdc, oldPen);
+  }
+}
+
+#define HIGHLIGHT_PEN 0
+#define PREMOVE_PEN   1
+
+VOID
+DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
+{
+  int x1, y1;
+  HPEN oldPen, hPen;
+  if (lineGap == 0) return;
+  if (flipView) {
+    x1 = boardRect.left +
+      lineGap/2 + ((BOARD_SIZE-1)-x) * (squareSize + lineGap);
+    y1 = boardRect.top +
+      lineGap/2 + y * (squareSize + lineGap);
+  } else {
+    x1 = boardRect.left +
+      lineGap/2 + x * (squareSize + lineGap);
+    y1 = boardRect.top +
+      lineGap/2 + ((BOARD_SIZE-1)-y) * (squareSize + lineGap);
+  }
+  hPen = pen ? premovePen : highlightPen;
+  oldPen = SelectObject(hdc, on ? hPen : gridPen);
+  MoveToEx(hdc, x1, y1, NULL);
+  LineTo(hdc, x1 + squareSize + lineGap, y1);
+  LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);
+  LineTo(hdc, x1, y1 + squareSize + lineGap);
+  LineTo(hdc, x1, y1);
+  SelectObject(hdc, oldPen);
+}
+
+VOID
+DrawHighlightsOnDC(HDC hdc)
+{
+  int i;
+  for (i=0; i<2; i++) {
+    if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) 
+      DrawHighlightOnDC(hdc, TRUE,
+                       highlightInfo.sq[i].x, highlightInfo.sq[i].y,
+                       HIGHLIGHT_PEN);
+  }
+  for (i=0; i<2; i++) {
+    if (premoveHighlightInfo.sq[i].x >= 0 && 
+       premoveHighlightInfo.sq[i].y >= 0) {
+       DrawHighlightOnDC(hdc, TRUE,
+                         premoveHighlightInfo.sq[i].x, 
+                         premoveHighlightInfo.sq[i].y,
+                         PREMOVE_PEN);
+    }
+  }
+}
+
+/* Note: sqcolor is used only in monoMode */
+/* Note that this code is largely duplicated in woptions.c,
+   function DrawSampleSquare, so that needs to be updated too */
+VOID
+DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)
+{
+  HBITMAP oldBitmap;
+  HBRUSH oldBrush;
+
+  if (appData.blindfold) return;
+
+  if (appData.monoMode) {
+    SelectObject(tmphdc, PieceBitmap(piece, 
+      color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));
+    BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,
+          sqcolor ? SRCCOPY : NOTSRCCOPY);
+  } else {
+    if (color) {
+      oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));
+      oldBrush = SelectObject(hdc, whitePieceBrush);
+      BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
+#if 0
+      /* Use black piece color for outline of white pieces */
+      /* Not sure this looks really good (though xboard does it).
+        Maybe better to have another selectable color, default black */
+      SelectObject(hdc, blackPieceBrush); /* could have own brush */
+      SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
+      BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
+#else
+      /* Use black for outline of white pieces */
+      SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));
+      BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, SRCAND);
+#endif
+    } else {
+#if 0
+      /* Use white piece color for details of black pieces */
+      /* Requires filled-in solid bitmaps (BLACK_PIECE class); the
+        WHITE_PIECE ones aren't always the right shape. */
+      /* Not sure this looks really good (though xboard does it).
+        Maybe better to have another selectable color, default medium gray? */
+      oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));
+      oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */
+      BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
+      SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
+      SelectObject(hdc, blackPieceBrush);
+      BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
+#else
+      /* Use square color for details of black pieces */
+      oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));
+      oldBrush = SelectObject(hdc, blackPieceBrush);
+      BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0, 0x00B8074A);
+#endif
+    }
+    SelectObject(hdc, oldBrush);
+    SelectObject(tmphdc, oldBitmap);
+  }
+}
+
+VOID
+DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)
+{
+  int row, column, x, y, square_color, piece_color;
+  ChessSquare piece;
+  HBRUSH oldBrush;
+
+  for (row = 0; row < BOARD_SIZE; row++) {
+    for (column = 0; column < BOARD_SIZE; column++) {
+  
+      SquareToPos(row, column, &x, &y);
+
+      piece = board[row][column];
+
+      square_color = ((column + row) % 2) == 1;
+      piece_color = (int) piece < (int) BlackPawn;
+
+      if (appData.monoMode) {
+        if (piece == EmptySquare) {
+          BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,
+                square_color ? WHITENESS : BLACKNESS);
+        } else {
+          DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);
+        }
+      } else {
+        oldBrush = SelectObject(hdc, square_color ?
+                               lightSquareBrush : darkSquareBrush);
+        BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);
+        SelectObject(hdc, oldBrush);
+        if (piece != EmptySquare)
+          DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);
+      }
+    }
+  }
+}
+
+#define MAX_CLIPS 200   /* more than enough */
+
+VOID
+HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
+{
+  static Board lastReq, lastDrawn;
+  static HighlightInfo lastDrawnHighlight, lastDrawnPremove;
+  static int lastDrawnFlipView = 0;
+  static int lastReqValid = 0, lastDrawnValid = 0;
+  int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;
+  HDC tmphdc;
+  HDC hdcmem;
+  HBITMAP bufferBitmap;
+  HBITMAP oldBitmap;
+  RECT Rect;
+  HRGN clips[MAX_CLIPS];
+  ChessSquare dragged_piece = EmptySquare;
+
+  /* I'm undecided on this - this function figures out whether a full
+   * repaint is necessary on its own, so there's no real reason to have the
+   * caller tell it that.  I think this can safely be set to FALSE - but
+   * if we trust the callers not to request full repaints unnessesarily, then
+   * we could skip some clipping work.  In other words, only request a full
+   * redraw when the majority of pieces have changed positions (ie. flip, 
+   * gamestart and similar)  --Hawk
+   */
+  Boolean fullrepaint = repaint;
+
+  if (board == NULL) {
+    if (!lastReqValid) {
+      return;
+    }
+    board = lastReq;
+  } else {
+    CopyBoard(lastReq, board);
+    lastReqValid = 1;
+  }
+
+  if (doingSizing) {
+    return;
+  }
+
+  if (IsIconic(hwndMain)) {
+    return;
+  }
+
+  if (hdc == NULL) {
+    hdc = GetDC(hwndMain);
+    if (!appData.monoMode) {
+      SelectPalette(hdc, hPal, FALSE);
+      RealizePalette(hdc);
+    }
+    releaseDC = TRUE;
+  } else {
+    releaseDC = FALSE;
+  }
+
+#if 0
+  fprintf(debugFP, "*******************************\n"
+                   "repaint = %s\n"
+                   "dragInfo.from (%d,%d)\n"
+                   "dragInfo.start (%d,%d)\n"
+                   "dragInfo.pos (%d,%d)\n"
+                   "dragInfo.lastpos (%d,%d)\n", 
+                    repaint ? "TRUE" : "FALSE",
+                    dragInfo.from.x, dragInfo.from.y, 
+                    dragInfo.start.x, dragInfo.start.y,
+                    dragInfo.pos.x, dragInfo.pos.y,
+                    dragInfo.lastpos.x, dragInfo.lastpos.y);
+  fprintf(debugFP, "prev:  ");
+  for (row = 0; row < 8; row++) {
+    for (column = 0; column < 8; column++) {
+      fprintf(debugFP, "%d ", lastDrawn[row][column]);
+    }
+  }
+  fprintf(debugFP, "\n");
+  fprintf(debugFP, "board: ");
+  for (row = 0; row < 8; row++) {
+    for (column = 0; column < 8; column++) {
+      fprintf(debugFP, "%d ", board[row][column]);
+    }
+  }
+  fprintf(debugFP, "\n");
+  fflush(debugFP);
+#endif
+
+  /* Create some work-DCs */
+  hdcmem = CreateCompatibleDC(hdc);
+  tmphdc = CreateCompatibleDC(hdc);
+
+  /* Figure out which squares need updating by comparing the 
+   * newest board with the last drawn board and checking if
+   * flipping has changed.
+   */
+  if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {
+    for (row = 0; row < 8; row++) {
+      for (column = 0; column < 8; column++) {
+       if (lastDrawn[row][column] != board[row][column]) {
+         SquareToPos(row, column, &x, &y);
+         clips[num_clips++] =
+           CreateRectRgn(x, y, x + squareSize, y + squareSize);
+       }
+      }
+    }
+    for (i=0; i<2; i++) {
+      if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||
+         lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {
+       if (lastDrawnHighlight.sq[i].x >= 0 &&
+           lastDrawnHighlight.sq[i].y >= 0) {
+         SquareToPos(lastDrawnHighlight.sq[i].y,
+                     lastDrawnHighlight.sq[i].x, &x, &y);
+         clips[num_clips++] =
+           CreateRectRgn(x - lineGap, y - lineGap, 
+                         x + squareSize + lineGap, y + squareSize + lineGap);
+       }
+       if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {
+         SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);
+         clips[num_clips++] =
+           CreateRectRgn(x - lineGap, y - lineGap, 
+                         x + squareSize + lineGap, y + squareSize + lineGap);
+       }
+      }
+    }
+    for (i=0; i<2; i++) {
+      if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||
+         lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {
+       if (lastDrawnPremove.sq[i].x >= 0 &&
+           lastDrawnPremove.sq[i].y >= 0) {
+         SquareToPos(lastDrawnPremove.sq[i].y,
+                     lastDrawnPremove.sq[i].x, &x, &y);
+         clips[num_clips++] =
+           CreateRectRgn(x - lineGap, y - lineGap, 
+                         x + squareSize + lineGap, y + squareSize + lineGap);
+       }
+       if (premoveHighlightInfo.sq[i].x >= 0 && 
+           premoveHighlightInfo.sq[i].y >= 0) {
+         SquareToPos(premoveHighlightInfo.sq[i].y, 
+                     premoveHighlightInfo.sq[i].x, &x, &y);
+         clips[num_clips++] =
+           CreateRectRgn(x - lineGap, y - lineGap, 
+                         x + squareSize + lineGap, y + squareSize + lineGap);
+       }
+      }
+    }
+  } else {
+    fullrepaint = TRUE;
+  }
+
+  /* Create a buffer bitmap - this is the actual bitmap
+   * being written to.  When all the work is done, we can
+   * copy it to the real DC (the screen).  This avoids
+   * the problems with flickering.
+   */
+  GetClientRect(hwndMain, &Rect);
+  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,
+                                       Rect.bottom-Rect.top+1);
+  oldBitmap = SelectObject(hdcmem, bufferBitmap);
+  if (!appData.monoMode) {
+    SelectPalette(hdcmem, hPal, FALSE);
+  }
+
+  /* Create clips for dragging */
+  if (!fullrepaint) {
+    if (dragInfo.from.x >= 0) {
+      SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);
+      clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
+    }
+    if (dragInfo.start.x >= 0) {
+      SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);
+      clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
+    }
+    if (dragInfo.pos.x >= 0) {
+      x = dragInfo.pos.x - squareSize / 2;
+      y = dragInfo.pos.y - squareSize / 2;
+      clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
+    }
+    if (dragInfo.lastpos.x >= 0) {
+      x = dragInfo.lastpos.x - squareSize / 2;
+      y = dragInfo.lastpos.y - squareSize / 2;
+      clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);
+    }
+  }
+
+  /* If dragging is in progress, we temporarely remove the piece */
+  if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {
+    dragged_piece = board[dragInfo.from.y][dragInfo.from.x];
+    board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;
+  }
+
+  /* Are we animating a move?  
+   * If so, 
+   *   - remove the piece from the board (temporarely)
+   *   - calculate the clipping region
+   */
+  if (!fullrepaint) {
+    if (animInfo.piece != EmptySquare) {
+      board[animInfo.from.y][animInfo.from.x] = EmptySquare;
+      x = boardRect.left + animInfo.lastpos.x;
+      y = boardRect.top + animInfo.lastpos.y;
+      x2 = boardRect.left + animInfo.pos.x;
+      y2 = boardRect.top + animInfo.pos.y;
+      clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);
+      /* Slight kludge.  The real problem is that after AnimateMove is
+        done, the position on the screen does not match lastDrawn.
+        This currently causes trouble only on e.p. captures in
+        atomic, where the piece moves to an empty square and then
+        explodes.  The old and new positions both had an empty square
+        at the destination, but animation has drawn a piece there and
+        we have to remember to erase it. */
+      lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;
+    }
+  }
+
+  /* No clips?  Make sure we have fullrepaint set to TRUE */
+  if (num_clips == 0)
+    fullrepaint = TRUE;
+
+  /* Set clipping on the memory DC */
+  if (!fullrepaint) {
+    SelectClipRgn(hdcmem, clips[0]);
+    for (x = 1; x < num_clips; x++) {
+      if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)
+        abort();  // this should never ever happen!
+    }
+  }
+
+  /* Do all the drawing to the memory DC */
+  DrawGridOnDC(hdcmem);
+  DrawHighlightsOnDC(hdcmem);
+  DrawBoardOnDC(hdcmem, board, tmphdc);
+  DrawCoordsOnDC(hdcmem);
+
+  /* Put the dragged piece back into place and draw it */
+  if (dragged_piece != EmptySquare) {
+    board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;
+    x = dragInfo.pos.x - squareSize / 2;
+    y = dragInfo.pos.y - squareSize / 2;
+    DrawPieceOnDC(hdcmem, dragged_piece,
+                 ((int) dragged_piece < (int) BlackPawn), 
+                  (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);
+  }   
+  
+  /* Put the animated piece back into place and draw it */
+  if (animInfo.piece != EmptySquare) {
+    board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;
+    x = boardRect.left + animInfo.pos.x;
+    y = boardRect.top + animInfo.pos.y;
+    DrawPieceOnDC(hdcmem, animInfo.piece,
+                 ((int) animInfo.piece < (int) BlackPawn),
+                  (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);
+  }
+
+  /* Release the bufferBitmap by selecting in the old bitmap 
+   * and delete the memory DC
+   */
+  SelectObject(hdcmem, oldBitmap);
+  DeleteDC(hdcmem);
+
+  /* Set clipping on the target DC */
+  if (!fullrepaint) {
+    SelectClipRgn(hdc, clips[0]);
+    for (x = 1; x < num_clips; x++) {
+      if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)
+        abort();   // this should never ever happen!
+    } 
+  }
+
+  /* Copy the new bitmap onto the screen in one go.
+   * This way we avoid any flickering
+   */
+  oldBitmap = SelectObject(tmphdc, bufferBitmap);
+  BitBlt(hdc, boardRect.left, boardRect.top,
+        boardRect.right - boardRect.left,
+        boardRect.bottom - boardRect.top,
+        tmphdc, boardRect.left, boardRect.top, SRCCOPY);
+  SelectObject(tmphdc, oldBitmap);
+
+  /* Massive cleanup */
+  for (x = 0; x < num_clips; x++)
+    DeleteObject(clips[x]);
+
+  DeleteDC(tmphdc);
+  DeleteObject(bufferBitmap);
+
+  if (releaseDC) 
+    ReleaseDC(hwndMain, hdc);
+  
+  if (lastDrawnFlipView != flipView) {
+    if (flipView)
+      CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);
+    else
+      CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);
+  }
+
+  CopyBoard(lastDrawn, board);
+  lastDrawnHighlight = highlightInfo;
+  lastDrawnPremove   = premoveHighlightInfo;
+  lastDrawnFlipView = flipView;
+  lastDrawnValid = 1;
+}
+
+
+/*---------------------------------------------------------------------------*\
+| CLIENT PAINT PROCEDURE
+|   This is the main event-handler for the WM_PAINT message.
+|
+\*---------------------------------------------------------------------------*/
+VOID
+PaintProc(HWND hwnd)
+{
+  HDC         hdc;
+  PAINTSTRUCT ps;
+  HFONT       oldFont;
+
+  if(hdc = BeginPaint(hwnd, &ps)) {
+    if (IsIconic(hwnd)) {
+      DrawIcon(hdc, 2, 2, iconCurrent);
+    } else {
+      if (!appData.monoMode) {
+       SelectPalette(hdc, hPal, FALSE);
+       RealizePalette(hdc);
+      }
+      HDCDrawPosition(hdc, 1, NULL);
+      oldFont =
+       SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
+      ExtTextOut(hdc, messageRect.left, messageRect.top,
+                ETO_CLIPPED|ETO_OPAQUE,
+                &messageRect, messageText, strlen(messageText), NULL);
+      SelectObject(hdc, oldFont);
+      DisplayBothClocks();
+    }
+    EndPaint(hwnd,&ps);
+  }
+
+  return;
+}
+
+
+/*
+ * If the user selects on a border boundary, return -1; if off the board,
+ *   return -2.  Otherwise map the event coordinate to the square.
+ * The offset boardRect.left or boardRect.top must already have been
+ *   subtracted from x.
+ */
+int
+EventToSquare(int x)
+{
+  if (x <= 0)
+    return -2;
+  if (x < lineGap)
+    return -1;
+  x -= lineGap;
+  if ((x % (squareSize + lineGap)) >= squareSize)
+    return -1;
+  x /= (squareSize + lineGap);
+  if (x >= BOARD_SIZE)
+    return -2;
+  return x;
+}
+
+typedef struct {
+  char piece;
+  int command;
+  char* name;
+} DropEnable;
+
+DropEnable dropEnables[] = {
+  { 'P', DP_Pawn, "Pawn" },
+  { 'N', DP_Knight, "Knight" },
+  { 'B', DP_Bishop, "Bishop" },
+  { 'R', DP_Rook, "Rook" },
+  { 'Q', DP_Queen, "Queen" },
+};
+
+VOID
+SetupDropMenu(HMENU hmenu)
+{
+  int i, count, enable;
+  char *p;
+  extern char white_holding[], black_holding[];
+  char item[MSG_SIZ];
+
+  for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {
+    p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
+              dropEnables[i].piece);
+    count = 0;
+    while (p && *p++ == dropEnables[i].piece) count++;
+    sprintf(item, "%s  %d", dropEnables[i].name, count);
+    enable = count > 0 || !appData.testLegality
+      /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
+                     && !appData.icsActive);
+    ModifyMenu(hmenu, dropEnables[i].command,
+              MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,
+              dropEnables[i].command, item);
+  }
+}
+
+static int fromX = -1, fromY = -1, toX, toY;
+
+/* Event handler for mouse messages */
+VOID
+MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  int x, y;
+  POINT pt;
+  static int recursive = 0;
+  HMENU hmenu;
+  BOOLEAN saveAnimate;
+  static BOOLEAN sameAgain = FALSE;
+
+  if (recursive) {
+    if (message == WM_MBUTTONUP) {
+      /* Hideous kludge to fool TrackPopupMenu into paying attention
+        to the middle button: we simulate pressing the left button too!
+        */
+      PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);
+      PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);
+    }
+    return;
+  }
+  recursive++;
+  
+  pt.x = LOWORD(lParam);
+  pt.y = HIWORD(lParam);
+  x = EventToSquare(pt.x - boardRect.left);
+  y = EventToSquare(pt.y - boardRect.top);
+  if (!flipView && y >= 0) {
+    y = BOARD_SIZE - 1 - y;
+  }
+  if (flipView && x >= 0) {
+    x = BOARD_SIZE - 1 - x;
+  }
+
+  switch (message) {
+  case WM_LBUTTONDOWN:
+    ErrorPopDown();
+    sameAgain = FALSE;
+    if (y == -2) {
+      /* Downclick vertically off board; check if on clock */
+      if (PtInRect((LPRECT) &whiteRect, pt)) {
+       if (gameMode == EditPosition) {
+         SetWhiteToPlayEvent();
+       } else if (gameMode == IcsPlayingBlack ||
+                  gameMode == MachinePlaysWhite) {
+         CallFlagEvent();
+       }
+      } else if (PtInRect((LPRECT) &blackRect, pt)) {
+       if (gameMode == EditPosition) {
+         SetBlackToPlayEvent();
+       } else if (gameMode == IcsPlayingWhite ||
+                  gameMode == MachinePlaysBlack) {
+         CallFlagEvent();
+       }
+      }
+      if (!appData.highlightLastMove) {
+        ClearHighlights();
+       DrawPosition(FALSE, NULL);
+      }
+      fromX = fromY = -1;
+      dragInfo.start.x = dragInfo.start.y = -1;
+      dragInfo.from = dragInfo.start;
+      break;
+    } else if (x < 0 || y < 0) {
+      break;
+    } else if (fromX == x && fromY == y) {
+      /* Downclick on same square again */
+      ClearHighlights();
+      DrawPosition(FALSE, NULL);
+      sameAgain = TRUE;  
+    } else if (fromX != -1) {
+      /* Downclick on different square */
+      ChessSquare pdown, pup;
+      pdown = boards[currentMove][fromY][fromX];
+      pup = boards[currentMove][y][x];
+      if (gameMode == EditPosition ||
+         !((WhitePawn <= pdown && pdown <= WhiteKing &&
+            WhitePawn <= pup && pup <= WhiteKing) ||
+           (BlackPawn <= pdown && pdown <= BlackKing &&
+            BlackPawn <= pup && pup <= BlackKing))) {
+       /* EditPosition, empty square, or different color piece;
+          click-click move is possible */
+       toX = x;
+       toY = y;
+       if (IsPromotion(fromX, fromY, toX, toY)) {
+         if (appData.alwaysPromoteToQueen) {
+           UserMoveEvent(fromX, fromY, toX, toY, 'q');
+           if (!appData.highlightLastMove) {
+             ClearHighlights();
+             DrawPosition(FALSE, NULL);
+           }
+         } else {
+           SetHighlights(fromX, fromY, toX, toY);
+           DrawPosition(FALSE, NULL);
+           PromotionPopup(hwnd);
+         }
+       } else {        /* not a promotion */
+         if (appData.animate || appData.highlightLastMove) {
+           SetHighlights(fromX, fromY, toX, toY);
+         } else {
+           ClearHighlights();
+         }
+         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
+         if (appData.animate && !appData.highlightLastMove) {
+           ClearHighlights();
+           DrawPosition(FALSE, NULL);
+         }
+       }
+       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
+       fromX = fromY = -1;
+       break;
+      }
+      ClearHighlights();
+      DrawPosition(FALSE, NULL);
+    }
+    /* First downclick, or restart on a square with same color piece */
+    if (!frozen && OKToStartUserMove(x, y)) {
+      fromX = x;
+      fromY = y;
+      dragInfo.lastpos = pt;
+      dragInfo.from.x = fromX;
+      dragInfo.from.y = fromY;
+      dragInfo.start = dragInfo.from;
+      SetCapture(hwndMain);
+    } else {
+      fromX = fromY = -1;
+      dragInfo.start.x = dragInfo.start.y = -1;
+      dragInfo.from = dragInfo.start;
+    }
+    break;
+
+  case WM_LBUTTONUP:
+    ReleaseCapture();
+    if (fromX == -1) break;
+    if (x == fromX && y == fromY) {
+      dragInfo.from.x = dragInfo.from.y = -1;
+      /* Upclick on same square */
+      if (sameAgain) {
+       /* Clicked same square twice: abort click-click move */
+       fromX = fromY = -1;
+       gotPremove = 0;
+       ClearPremoveHighlights();
+      } else {
+       /* First square clicked: start click-click move */
+       SetHighlights(fromX, fromY, -1, -1);
+      }
+      DrawPosition(FALSE, NULL);
+    } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {
+      /* Errant click; ignore */
+      break;
+    } else {
+      /* Finish drag move */
+      dragInfo.from.x = dragInfo.from.y = -1;
+      toX = x;
+      toY = y;
+      saveAnimate = appData.animate; /* sorry, Hawk :) */
+      appData.animate = appData.animate && !appData.animateDragging;
+      if (IsPromotion(fromX, fromY, toX, toY)) {
+       if (appData.alwaysPromoteToQueen) {
+         UserMoveEvent(fromX, fromY, toX, toY, 'q');
+       } else {
+         DrawPosition(FALSE, NULL);
+         PromotionPopup(hwnd);
+       }
+      } else {
+       UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
+      }
+      if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
+      appData.animate = saveAnimate;
+      fromX = fromY = -1;
+      if (appData.highlightDragging && !appData.highlightLastMove) {
+       ClearHighlights();
+      }
+      if (appData.animate || appData.animateDragging ||
+         appData.highlightDragging || gotPremove) {
+       DrawPosition(FALSE, NULL);
+      }
+    }
+    dragInfo.start.x = dragInfo.start.y = -1; 
+    dragInfo.pos = dragInfo.lastpos = dragInfo.start;
+    break;
+
+  case WM_MOUSEMOVE:
+    if ((appData.animateDragging || appData.highlightDragging)
+       && (wParam & MK_LBUTTON)
+       && dragInfo.from.x >= 0) {
+      if (appData.animateDragging) {
+       dragInfo.pos = pt;
+      }
+      if (appData.highlightDragging) {
+       SetHighlights(fromX, fromY, x, y);
+      }
+      DrawPosition(FALSE, NULL);
+      dragInfo.lastpos = dragInfo.pos;
+    }
+    break;
+  case WM_MOUSEWHEEL:
+      {
+         signed short u = HIWORD(wParam);
+                fprintf(debugFP, "mouse\n");
+       /* Example: if the mouse wheel is brought forward one click, u is 120. Two clicks, its 240.
+          if the mouse wheel is brought back one click, its -120, two clicks, -240, etc. */
+  
+        if (u && !(u%WHEEL_DELTA)) {
+                        while(u) {
+                               if (u>0) { 
+                                       u -= WHEEL_DELTA; 
+                                       ForwardEvent(); 
+                                       SetFocus(hwndMain);
+                               } else { 
+                                       u+= WHEEL_DELTA; 
+                                       BackwardEvent(); 
+                                       SetFocus(hwndMain);
+                               }
+                       }
+               }
+         }
+      break;
+  case WM_MBUTTONDOWN:
+  case WM_RBUTTONDOWN:
+    ErrorPopDown();
+    ReleaseCapture();
+    fromX = fromY = -1;
+    dragInfo.pos.x = dragInfo.pos.y = -1;
+    dragInfo.start.x = dragInfo.start.y = -1;
+    dragInfo.from = dragInfo.start;
+    dragInfo.lastpos = dragInfo.pos;
+    if (appData.highlightDragging) {
+      ClearHighlights();
+    }
+    DrawPosition(TRUE, NULL);
+
+    switch (gameMode) {
+    case EditPosition:
+    case IcsExamining:
+      if (x < 0 || y < 0) break;
+      fromX = x;
+      fromY = y;
+      if (message == WM_MBUTTONDOWN) {
+       buttonCount = 3;  /* even if system didn't think so */
+       if (wParam & MK_SHIFT) 
+         MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
+       else
+         MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
+      } else { /* message == WM_RBUTTONDOWN */
+#if 0
+       if (buttonCount == 3) {
+         if (wParam & MK_SHIFT) 
+           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);
+         else
+           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);
+       } else {
+         MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
+       }
+#else
+       /* Just have one menu, on the right button.  Windows users don't
+          think to try the middle one, and sometimes other software steals
+          it, or it doesn't really exist. */
+       MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);
+#endif
+      }
+      break;
+    case IcsPlayingWhite:
+    case IcsPlayingBlack:
+    case EditGame:
+    case MachinePlaysWhite:
+    case MachinePlaysBlack:
+      if (appData.testLegality &&
+         gameInfo.variant != VariantBughouse &&
+         gameInfo.variant != VariantCrazyhouse) break;
+      if (x < 0 || y < 0) break;
+      fromX = x;
+      fromY = y;
+      hmenu = LoadMenu(hInst, "DropPieceMenu");
+      SetupDropMenu(hmenu);
+      MenuPopup(hwnd, pt, hmenu, -1);
+      break;
+    default:
+      break;
+    }
+    break;
+  }
+
+  recursive--;
+}
+
+/* Preprocess messages for buttons in main window */
+LRESULT CALLBACK
+ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  int id = GetWindowLong(hwnd, GWL_ID);
+  int i, dir;
+
+  for (i=0; i<N_BUTTONS; i++) {
+    if (buttonDesc[i].id == id) break;
+  }
+  if (i == N_BUTTONS) return 0;
+  switch (message) {
+  case WM_KEYDOWN:
+    switch (wParam) {
+    case VK_LEFT:
+    case VK_RIGHT:
+      dir = (wParam == VK_LEFT) ? -1 : 1;
+      SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);
+      return TRUE;
+    }
+    break;
+  case WM_CHAR:
+    switch (wParam) {
+    case '\r':
+      SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);
+      return TRUE;
+    case '\t':
+      if (appData.icsActive) {
+       if (GetKeyState(VK_SHIFT) < 0) {
+         /* shifted */
+         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
+         SetFocus(h);
+       } else {
+         /* unshifted */
+         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
+         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
+         SetFocus(h);
+       }
+       return TRUE;
+      }
+      break;
+    default:
+      if (appData.icsActive) {
+        HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+       if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
+       SetFocus(h);
+       SendMessage(h, WM_CHAR, wParam, lParam);
+       return TRUE;
+      } else if (isalpha((char)wParam) || isdigit((char)wParam)){
+       PopUpMoveDialog((char)wParam);
+      }
+      break;
+    }
+    break;
+  }
+  return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);
+}
+
+/* Process messages for Promotion dialog box */
+LRESULT CALLBACK
+Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  char promoChar;
+
+  switch (message) {
+  case WM_INITDIALOG: /* message: initialize dialog box */
+    /* Center the dialog over the application window */
+    CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
+    ShowWindow(GetDlgItem(hDlg, PB_King), 
+      (!appData.testLegality || gameInfo.variant == VariantSuicide) ?
+              SW_SHOW : SW_HIDE);
+    return TRUE;
+
+  case WM_COMMAND: /* message: received a command */
+    switch (LOWORD(wParam)) {
+    case IDCANCEL:
+      EndDialog(hDlg, TRUE); /* Exit the dialog */
+      ClearHighlights();
+      DrawPosition(FALSE, NULL);
+      return TRUE;
+    case PB_King:
+      promoChar = 'k';
+      break;
+    case PB_Queen:
+      promoChar = 'q';
+      break;
+    case PB_Rook:
+      promoChar = 'r';
+      break;
+    case PB_Bishop:
+      promoChar = 'b';
+      break;
+    case PB_Knight:
+      promoChar = 'n';
+      break;
+    default:
+      return FALSE;
+    }
+    EndDialog(hDlg, TRUE); /* Exit the dialog */
+    UserMoveEvent(fromX, fromY, toX, toY, promoChar);
+    if (!appData.highlightLastMove) {
+      ClearHighlights();
+      DrawPosition(FALSE, NULL);
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/* Pop up promotion dialog */
+VOID
+PromotionPopup(HWND hwnd)
+{
+  FARPROC lpProc;
+
+  lpProc = MakeProcInstance((FARPROC)Promotion, hInst);
+  DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),
+    hwnd, (DLGPROC)lpProc);
+  FreeProcInstance(lpProc);
+}
+
+/* Toggle ShowThinking */
+VOID
+ToggleShowThinking()
+{
+  ShowThinkingEvent(!appData.showThinking);
+}
+
+VOID
+LoadGameDialog(HWND hwnd, char* title)
+{
+  UINT number = 0;
+  FILE *f;
+  char fileTitle[MSG_SIZ];
+  f = OpenFileDialog(hwnd, FALSE, "",
+                    appData.oldSaveStyle ? "gam" : "pgn",
+                    GAME_FILT,
+                    title, &number, fileTitle, NULL);
+  if (f != NULL) {
+    cmailMsgLoaded = FALSE;
+    if (number == 0) {
+      int error = GameListBuild(f);
+      if (error) {
+        DisplayError("Cannot build game list", error);
+      } else if (!ListEmpty(&gameList) &&
+                 ((ListGame *) gameList.tailPred)->number > 1) {
+       GameListPopUp(f, fileTitle);
+        return;
+      }
+      GameListDestroy();
+      number = 1;
+    }
+    LoadGame(f, number, fileTitle, FALSE);
+  }
+}
+
+VOID
+ChangedConsoleFont()
+{
+  CHARFORMAT cfmt;
+  CHARRANGE tmpsel, sel;
+  MyFont *f = font[boardSize][CONSOLE_FONT];
+  HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
+  HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+  PARAFORMAT paraf;
+
+  cfmt.cbSize = sizeof(CHARFORMAT);
+  cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;
+  strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);
+  /* yHeight is expressed in twips.  A twip is 1/20 of a font's point
+   * size.  This was undocumented in the version of MSVC++ that I had
+   * when I wrote the code, but is apparently documented now.
+   */
+  cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);
+  cfmt.bCharSet = f->lf.lfCharSet;
+  cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;
+  SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); 
+  SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); 
+  /* Why are the following seemingly needed too? */
+  SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); 
+  SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); 
+  SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);
+  tmpsel.cpMin = 0;
+  tmpsel.cpMax = -1; /*999999?*/
+  SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);
+  SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); 
+  /* Trying putting this here too.  It still seems to tickle a RichEdit
+   *  bug: sometimes RichEdit indents the first line of a paragraph too.
+   */
+  paraf.cbSize = sizeof(paraf);
+  paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;
+  paraf.dxStartIndent = 0;
+  paraf.dxOffset = WRAP_INDENT;
+  SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);
+  SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ * Window Proc for main window
+ *
+\*---------------------------------------------------------------------------*/
+
+/* Process messages for main window, etc. */
+LRESULT CALLBACK
+WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  FARPROC lpProc;
+  int wmId, wmEvent;
+  char *defName;
+  FILE *f;
+  UINT number;
+  char fileTitle[MSG_SIZ];
+
+  switch (message) {
+
+  case WM_PAINT: /* message: repaint portion of window */
+    PaintProc(hwnd);
+    break;
+
+  case WM_ERASEBKGND:
+    if (IsIconic(hwnd)) {
+      /* Cheat; change the message */
+      return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));
+    } else {
+      return (DefWindowProc(hwnd, message, wParam, lParam));
+    }
+    break;
+
+  case WM_LBUTTONDOWN:
+  case WM_MBUTTONDOWN:
+  case WM_RBUTTONDOWN:
+  case WM_LBUTTONUP:
+  case WM_MBUTTONUP:
+  case WM_RBUTTONUP:
+  case WM_MOUSEMOVE:
+  case WM_MOUSEWHEEL:
+    MouseEvent(hwnd, message, wParam, lParam);
+    break;
+
+  case WM_CHAR:
+    
+    if (appData.icsActive) {
+      if (wParam == '\t') {
+       if (GetKeyState(VK_SHIFT) < 0) {
+         /* shifted */
+         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
+         SetFocus(h);
+       } else {
+         /* unshifted */
+         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleText);
+         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
+         SetFocus(h);
+       }
+      } else {
+       HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+       if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);
+       SetFocus(h);
+       SendMessage(h, message, wParam, lParam);
+      }
+    } else if (isalpha((char)wParam) || isdigit((char)wParam)) {
+      PopUpMoveDialog((char)wParam);
+    }
+    break;
+
+  case WM_PALETTECHANGED:
+    if (hwnd != (HWND)wParam && !appData.monoMode) {
+      int nnew;
+      HDC hdc = GetDC(hwndMain);
+      SelectPalette(hdc, hPal, TRUE);
+      nnew = RealizePalette(hdc);
+      if (nnew > 0) {
+       paletteChanged = TRUE;
+#if 0
+        UpdateColors(hdc);
+#else
+        InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/
+#endif
+      }
+      ReleaseDC(hwnd, hdc);
+    }
+    break;
+
+  case WM_QUERYNEWPALETTE:
+    if (!appData.monoMode /*&& paletteChanged*/) {
+      int nnew;
+      HDC hdc = GetDC(hwndMain);
+      paletteChanged = FALSE;
+      SelectPalette(hdc, hPal, FALSE);
+      nnew = RealizePalette(hdc);
+      if (nnew > 0) {
+       InvalidateRect(hwnd, &boardRect, FALSE);
+      }
+      ReleaseDC(hwnd, hdc);
+      return TRUE;
+    }
+    return FALSE;
+
+  case WM_COMMAND: /* message: command from application menu */
+    wmId    = LOWORD(wParam);
+    wmEvent = HIWORD(wParam);
+
+    switch (wmId) {    
+    case IDM_NewGame:
+      ResetGameEvent();
+         /* try without */
+      /*AnalysisPopDown(); */
+      break;
+
+    case IDM_LoadGame:
+      LoadGameDialog(hwnd, "Load Game from File");
+      break;
+
+    case IDM_LoadNextGame:
+      ReloadGame(1);
+      break;
+
+    case IDM_LoadPrevGame:
+      ReloadGame(-1);
+      break;
+
+    case IDM_ReloadGame:
+      ReloadGame(0);
+      break;
+
+    case IDM_LoadPosition:
+      if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
+        Reset(FALSE, TRUE);
+      }
+      number = 1;
+      f = OpenFileDialog(hwnd, FALSE, "",
+                        appData.oldSaveStyle ? "pos" : "fen",
+                        POSITION_FILT,
+                        "Load Position from File", &number, fileTitle, NULL);
+      if (f != NULL) {
+       LoadPosition(f, number, fileTitle);
+      }
+      break;
+
+    case IDM_LoadNextPosition:
+      ReloadPosition(1);
+      break;
+
+    case IDM_LoadPrevPosition:
+      ReloadPosition(-1);
+      break;
+
+    case IDM_ReloadPosition:
+      ReloadPosition(0);
+      break;
+
+    case IDM_SaveGame:
+      defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
+      f = OpenFileDialog(hwnd, TRUE, defName,
+                        appData.oldSaveStyle ? "gam" : "pgn",
+                        GAME_FILT,
+                        "Save Game to File", NULL, fileTitle, NULL);
+      if (f != NULL) {
+       SaveGame(f, 0, "");
+      }
+      break;
+
+    case IDM_SavePosition:
+      defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");
+      f = OpenFileDialog(hwnd, TRUE, defName,
+                        appData.oldSaveStyle ? "pos" : "fen",
+                        POSITION_FILT,
+                        "Save Position to File", NULL, fileTitle, NULL);
+      if (f != NULL) {
+       SavePosition(f, 0, "");
+      }
+      break;
+
+    case IDM_CopyGame:
+      CopyGameToClipboard();
+      break;
+
+    case IDM_PasteGame:
+      PasteGameFromClipboard();
+      break;
+
+    case IDM_CopyPosition:
+      CopyFENToClipboard();
+      break;
+
+    case IDM_PastePosition:
+      PasteFENFromClipboard();
+      break;
+
+    case IDM_MailMove:
+      MailMoveEvent();
+      break;
+
+    case IDM_ReloadCMailMsg:
+      Reset(TRUE, TRUE);
+      ReloadCmailMsgEvent(FALSE);
+      break;
+
+    case IDM_Minimize:
+      ShowWindow(hwnd, SW_MINIMIZE);
+      break;
+
+       case IDM_focus_EngineRoom:
+         SetFocus(analysisDialog);
+         break;
+
+       case IDM_focus_wb:
+         SetFocus(hwndMain);
+         break;
+
+    case IDM_Exit:
+      ExitEvent(0);
+      break;
+
+    case IDM_MachineWhite:
+      MachineWhiteEvent(); 
+         /*
+       * refresh the tags dialog only if it's visible
+       */
+      if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {
+         char *tags;
+         tags = PGNTags(&gameInfo);
+         TagsPopUp(tags, CmailMsg());
+         free(tags);
+      }
+      break;
+
+    case IDM_MachineBlack:
+      MachineBlackEvent(); 
+      /*
+       * refresh the tags dialog only if it's visible
+       */
+      if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {
+         char *tags;
+         tags = PGNTags(&gameInfo);
+         TagsPopUp(tags, CmailMsg());
+         free(tags);
+      }
+      break;
+
+    case IDM_TwoMachines:
+      TwoMachinesEvent();
+      /*
+       * refresh the tags dialog only if it's visible
+       */
+      if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {
+         char *tags;
+         tags = PGNTags(&gameInfo);
+         TagsPopUp(tags, CmailMsg());
+         free(tags);
+      }
+      break;
+
+    case IDM_AnalysisMode:
+      if (!first.analysisSupport) {
+        char buf[MSG_SIZ];
+        sprintf(buf, "%s does not support analysis", first.tidy);
+        DisplayError(buf, 0);
+      } else {
+       if (!appData.showThinking) ToggleShowThinking();
+       AnalyzeModeEvent();
+      }
+      break;
+
+    case IDM_AnalyzeFile:
+      if (!first.analysisSupport) {
+        char buf[MSG_SIZ];
+        sprintf(buf, "%s does not support analysis", first.tidy);
+        DisplayError(buf, 0);
+      } else {
+       if (!appData.showThinking) ToggleShowThinking();
+       AnalyzeFileEvent();
+       LoadGameDialog(hwnd, "Analyze Game from File");
+       AnalysisPeriodicEvent(1);
+      }
+      break;
+
+    case IDM_IcsClient:
+      IcsClientEvent();
+      break;
+
+    case IDM_EditGame:
+      EditGameEvent();
+      break;
+
+    case IDM_EditPosition:
+      EditPositionEvent();
+      break;
+
+    case IDM_Training:
+      TrainingEvent();
+      break;
+
+    case IDM_ShowGameList:
+      ShowGameListProc();
+      break;
+
+    case IDM_EditTags:
+      EditTagsProc();
+      break;
+
+    case IDM_EditComment:
+      if (commentDialogUp && editComment) {
+       CommentPopDown();
+      } else {
+       EditCommentEvent();
+      }
+      break;
+
+    case IDM_Pause:
+      PauseEvent();
+      break;
+
+    case IDM_Accept:
+      AcceptEvent();
+      break;
+
+    case IDM_Decline:
+      DeclineEvent();
+      break;
+
+    case IDM_Rematch:
+      RematchEvent();
+      break;
+
+    case IDM_CallFlag:
+      CallFlagEvent();
+      break;
+
+    case IDM_Draw:
+      DrawEvent();
+      break;
+
+    case IDM_Adjourn:
+      AdjournEvent();
+      break;
+
+    case IDM_Abort:
+      AbortEvent();
+      break;
+
+    case IDM_Resign:
+      ResignEvent();
+      break;
+
+    case IDM_StopObserving:
+      StopObservingEvent();
+      break;
+
+    case IDM_StopExamining:
+      StopExaminingEvent();
+      break;
+
+    case IDM_TypeInMove:
+      PopUpMoveDialog('\000');
+      break;
+
+    case IDM_Backward:
+      BackwardEvent();
+      SetFocus(hwndMain);
+      break;
+
+    case IDM_Forward:
+      ForwardEvent();
+      SetFocus(hwndMain);
+      break;
+
+    case IDM_ToStart:
+      ToStartEvent();
+      SetFocus(hwndMain);
+      break;
+
+    case IDM_ToEnd:
+      ToEndEvent();
+      SetFocus(hwndMain);
+      break;
+
+    case IDM_Revert:
+      RevertEvent();
+      break;
+
+    case IDM_TruncateGame:
+      TruncateGameEvent();
+      break;
+
+    case IDM_MoveNow:
+      MoveNowEvent();
+      break;
+
+    case IDM_RetractMove:
+      RetractMoveEvent();
+      break;
+
+    case IDM_FlipView:
+      flipView = !flipView;
+      DrawPosition(FALSE, NULL);
+      break;
+
+    case IDM_GeneralOptions:
+      GeneralOptionsPopup(hwnd);
+      break;
+
+    case IDM_BoardOptions:
+      BoardOptionsPopup(hwnd);
+      break;
+
+    case IDM_IcsOptions:
+      IcsOptionsPopup(hwnd);
+      break;
+
+       case IDM_ZippyDraw:
+         icsZippyDrawPopUp(hwnd);
+         break;
+
+    case IDM_Fonts:
+      FontsOptionsPopup(hwnd);
+      break;
+
+    case IDM_Sounds:
+      SoundOptionsPopup(hwnd);
+      break;
+
+    case IDM_CommPort:
+      CommPortOptionsPopup(hwnd);
+      break;
+
+    case IDM_LoadOptions:
+      LoadOptionsPopup(hwnd);
+      break;
+
+    case IDM_SaveOptions:
+      SaveOptionsPopup(hwnd);
+      break;
+
+    case IDM_TimeControl:
+      TimeControlOptionsPopup(hwnd);
+      break;
+
+    case IDM_SaveSettings:
+      SaveSettings(settingsFileName);
+      break;
+
+    case IDM_SaveSettingsOnExit:
+      saveSettingsOnExit = !saveSettingsOnExit;
+      (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,
+                          MF_BYCOMMAND|(saveSettingsOnExit ?
+                                        MF_CHECKED : MF_UNCHECKED));
+      break;
+
+    case IDM_Hint:
+      HintEvent();
+      break;
+
+    case IDM_Book:
+      BookEvent();
+      break;
+
+    case IDM_AboutGame:
+      AboutGameEvent();
+      break;
+
+    case IDM_Debug:
+      appData.debugMode = !appData.debugMode;
+      if (appData.debugMode) {
+       char dir[MSG_SIZ];
+       GetCurrentDirectory(MSG_SIZ, dir);
+       SetCurrentDirectory(installDir);
+       debugFP = fopen("WinBoard.debug", "w");
+        SetCurrentDirectory(dir);
+        setbuf(debugFP, NULL);
+      } else {
+       fclose(debugFP);
+        debugFP = NULL;
+      }
+      break;
+
+    case IDM_HELPCONTENTS:
+      if (!WinHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
+       MessageBox (GetFocus(),
+                   "Unable to activate help",
+                   szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
+      }
+      break;
+
+    case IDM_HELPSEARCH:
+      if (!WinHelp(hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"")) {
+       MessageBox (GetFocus(),
+                   "Unable to activate help",
+                   szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
+      }
+      break;
+
+    case IDM_HELPHELP:
+      if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {
+       MessageBox (GetFocus(),
+                   "Unable to activate help",
+                   szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
+      }
+      break;
+
+    case IDM_ABOUT:
+      lpProc = MakeProcInstance((FARPROC)About, hInst);
+      DialogBox(hInst, 
+       (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?
+       "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);
+      FreeProcInstance(lpProc);
+      break;
+
+    case IDM_DirectCommand1:
+      AskQuestionEvent("Direct Command",
+                      "Send to chess program:", "", "1");
+      break;
+    case IDM_DirectCommand2:
+      AskQuestionEvent("Direct Command",
+                      "Send to second chess program:", "", "2");
+      break;
+
+    case EP_WhitePawn:
+      EditPositionMenuEvent(WhitePawn, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_WhiteKnight:
+      EditPositionMenuEvent(WhiteKnight, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_WhiteBishop:
+      EditPositionMenuEvent(WhiteBishop, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_WhiteRook:
+      EditPositionMenuEvent(WhiteRook, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_WhiteQueen:
+      EditPositionMenuEvent(WhiteQueen, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_WhiteKing:
+      EditPositionMenuEvent(WhiteKing, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_BlackPawn:
+      EditPositionMenuEvent(BlackPawn, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_BlackKnight:
+      EditPositionMenuEvent(BlackKnight, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_BlackBishop:
+      EditPositionMenuEvent(BlackBishop, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_BlackRook:
+      EditPositionMenuEvent(BlackRook, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_BlackQueen:
+      EditPositionMenuEvent(BlackQueen, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_BlackKing:
+      EditPositionMenuEvent(BlackKing, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_EmptySquare:
+      EditPositionMenuEvent(EmptySquare, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_ClearBoard:
+      EditPositionMenuEvent(ClearBoard, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_White:
+      EditPositionMenuEvent(WhitePlay, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case EP_Black:
+      EditPositionMenuEvent(BlackPlay, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case DP_Pawn:
+      DropMenuEvent(WhitePawn, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case DP_Knight:
+      DropMenuEvent(WhiteKnight, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case DP_Bishop:
+      DropMenuEvent(WhiteBishop, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case DP_Rook:
+      DropMenuEvent(WhiteRook, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    case DP_Queen:
+      DropMenuEvent(WhiteQueen, fromX, fromY);
+      fromX = fromY = -1;
+      break;
+
+    default:
+      return (DefWindowProc(hwnd, message, wParam, lParam));
+    }
+    break;
+
+  case WM_TIMER:
+    switch (wParam) {
+    case CLOCK_TIMER_ID:
+      KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */
+      clockTimerEvent = 0;
+      DecrementClocks(); /* call into back end */
+      break;
+    case LOAD_GAME_TIMER_ID:
+      KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/
+      loadGameTimerEvent = 0;
+      AutoPlayGameLoop(); /* call into back end */
+      break;
+    case ANALYSIS_TIMER_ID:    
+       /* dirty hack without stat line and support */
+         if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
+                  appData.icsAnalyzeWindow || appData.AnalysisWindow) &&
+                  appData.periodicUpdates) {
+                               AnalysisPeriodicEvent(0);
+       } else {
+               KillTimer(hwnd, analysisTimerEvent);
+               analysisTimerEvent = 0;
+     }
+      break;
+    case DELAYED_TIMER_ID:
+      KillTimer(hwnd, delayedTimerEvent);
+      delayedTimerEvent = 0;
+      delayedTimerCallback();
+      break;
+    }
+    break;
+
+  case WM_USER_Input:
+    InputEvent(hwnd, message, wParam, lParam);
+    break;
+
+  case WM_ENTERSIZEMOVE:
+    if (hwnd == hwndMain) {
+      doingSizing = TRUE;
+      lastSizing = 0;
+    }
+    break;
+
+  case WM_SIZING:
+    if (hwnd == hwndMain) {
+      lastSizing = wParam;
+    }
+    break;
+
+  case WM_EXITSIZEMOVE:
+    if (hwnd == hwndMain) {
+      RECT client;
+      doingSizing = FALSE;
+      InvalidateRect(hwnd, &boardRect, FALSE);
+      GetClientRect(hwnd, &client);
+      ResizeBoard(client.right, client.bottom, lastSizing);
+      lastSizing = 0;
+    }
+    break;
+
+  case WM_DESTROY: /* message: window being destroyed */
+    PostQuitMessage(0);
+    break;
+
+  case WM_CLOSE:
+    if (hwnd == hwndMain) {
+      ExitEvent(0);
+    }
+    break;
+
+  default:     /* Passes it on if unprocessed */
+    return (DefWindowProc(hwnd, message, wParam, lParam));
+  }
+  return 0;
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ * Misc utility routines
+ *
+\*---------------------------------------------------------------------------*/
+
+/* 
+ * returns TRUE if user selects a different color, FALSE otherwise 
+ */
+
+BOOL
+ChangeColor(HWND hwnd, COLORREF *which)
+{
+  static BOOL firstTime = TRUE;
+  static DWORD customColors[16];
+  CHOOSECOLOR cc;
+  COLORREF newcolor;
+  int i;
+  ColorClass ccl;
+
+  if (firstTime) {
+    /* Make initial colors in use available as custom colors */
+    /* Should we put the compiled-in defaults here instead? */
+    i = 0;
+    customColors[i++] = lightSquareColor & 0xffffff;
+    customColors[i++] = darkSquareColor & 0xffffff;
+    customColors[i++] = whitePieceColor & 0xffffff;
+    customColors[i++] = blackPieceColor & 0xffffff;
+    customColors[i++] = highlightSquareColor & 0xffffff;
+    customColors[i++] = premoveHighlightColor & 0xffffff;
+
+    for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {
+      customColors[i++] = textAttribs[ccl].color;
+    }
+    while (i < 16) customColors[i++] = RGB(255, 255, 255);
+    firstTime = FALSE;
+  }
+
+  cc.lStructSize = sizeof(cc);
+  cc.hwndOwner = hwnd;
+  cc.hInstance = NULL;
+  cc.rgbResult = (DWORD) (*which & 0xffffff);
+  cc.lpCustColors = (LPDWORD) customColors;
+  cc.Flags = CC_RGBINIT|CC_FULLOPEN;
+
+  if (!ChooseColor(&cc)) return FALSE;
+
+  newcolor = (COLORREF) (0x2000000 | cc.rgbResult);
+  if (newcolor == *which) return FALSE;
+  *which = newcolor;
+  return TRUE;
+
+  /*
+  InitDrawingColors();
+  InvalidateRect(hwnd, &boardRect, FALSE);
+  */
+}
+
+BOOLEAN
+MyLoadSound(MySound *ms)
+{
+  BOOL ok = FALSE;
+  struct stat st;
+  FILE *f;
+
+  if (ms->data) free(ms->data);
+  ms->data = NULL;
+
+  switch (ms->name[0]) {
+  case NULLCHAR:
+    /* Silence */
+    ok = TRUE;
+    break;
+  case '$':
+    /* System sound from Control Panel.  Don't preload here. */
+    ok = TRUE;
+    break;
+  case '!':
+    if (ms->name[1] == NULLCHAR) {
+      /* "!" alone = silence */
+      ok = TRUE;
+    } else {
+      /* Builtin wave resource.  Error if not found. */
+      HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");
+      if (h == NULL) break;
+      ms->data = (void *)LoadResource(hInst, h);
+      if (h == NULL) break;
+      ok = TRUE;
+    }
+    break;
+  default:
+    /* .wav file.  Error if not found. */
+    f = fopen(ms->name, "rb");
+    if (f == NULL) break;
+    if (fstat(fileno(f), &st) < 0) break;
+    ms->data = malloc(st.st_size);
+    if (fread(ms->data, st.st_size, 1, f) < 1) break;
+    fclose(f);
+    ok = TRUE;
+    break;
+  }
+  if (!ok) {
+    char buf[MSG_SIZ];
+    sprintf(buf, "Error loading sound %s", ms->name);
+    DisplayError(buf, GetLastError());
+  }
+  return ok;
+}
+
+BOOLEAN
+MyPlaySound(MySound *ms)
+{
+  BOOLEAN ok = FALSE;
+  switch (ms->name[0]) {
+  case NULLCHAR:
+    /* Silence */
+    ok = TRUE;
+    break;
+  case '$':
+    /* System sound from Control Panel (deprecated feature).
+       "$" alone or an unset sound name gets default beep (still in use). */
+    if (ms->name[1]) {
+      ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);
+    }
+    if (!ok) ok = MessageBeep(MB_OK);
+    break; 
+  case '!':
+    /* Builtin wave resource, or "!" alone for silence */
+    if (ms->name[1]) {
+      if (ms->data == NULL) return FALSE;
+      ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
+    } else {
+      ok = TRUE;
+    }
+    break;
+  default:
+    /* .wav file.  Error if not found. */
+    if (ms->data == NULL) return FALSE;
+    ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);
+    break;
+  }
+  /* Don't print an error: this can happen innocently if the sound driver
+     is busy; for instance, if another instance of WinBoard is playing
+     a sound at about the same time. */
+#if 0
+  if (!ok) {
+    char buf[MSG_SIZ];
+    sprintf(buf, "Error playing sound %s", ms->name);
+    DisplayError(buf, GetLastError());
+  }
+#endif
+  return ok;
+}
+
+
+LRESULT CALLBACK
+OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  BOOL ok;
+  OPENFILENAME *ofn;
+  static UINT *number; /* gross that this is static */
+
+  switch (message) {
+  case WM_INITDIALOG: /* message: initialize dialog box */
+    /* Center the dialog over the application window */
+    ofn = (OPENFILENAME *) lParam;
+    if (ofn->Flags & OFN_ENABLETEMPLATE) {
+      number = (UINT *) ofn->lCustData;
+      SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");
+    } else {
+      number = NULL;
+    }
+    CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+    return FALSE;  /* Allow for further processing */
+
+  case WM_COMMAND:
+    if ((LOWORD(wParam) == IDOK) && (number != NULL)) {
+      *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);
+    }
+    return FALSE;  /* Allow for further processing */
+  }
+  return FALSE;
+}
+
+UINT APIENTRY
+OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
+{
+  static UINT *number;
+  OPENFILENAME *ofname;
+  OFNOTIFY *ofnot;
+  switch (uiMsg) {
+  case WM_INITDIALOG:
+    ofname = (OPENFILENAME *)lParam;
+    number = (UINT *)(ofname->lCustData);
+    break;
+  case WM_NOTIFY:
+    ofnot = (OFNOTIFY *)lParam;
+    if (ofnot->hdr.code == CDN_FILEOK) {
+      *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);
+    }
+    break;
+  }
+  return 0;
+}
+
+
+FILE *
+OpenFileDialog(HWND hwnd, BOOL write, char *defName, char *defExt,
+              char *nameFilt, char *dlgTitle, UINT *number,
+              char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])
+{
+  OPENFILENAME openFileName;
+  char buf1[MSG_SIZ];
+  FILE *f;
+
+  if (fileName == NULL) fileName = buf1;
+  if (defName == NULL) {
+    strcpy(fileName, "*.");
+    strcat(fileName, defExt);
+  } else {
+    strcpy(fileName, defName);
+  }
+  if (fileTitle) strcpy(fileTitle, "");
+  if (number) *number = 0;
+
+  openFileName.lStructSize       = sizeof(OPENFILENAME);
+  openFileName.hwndOwner         = hwnd;
+  openFileName.hInstance         = (HANDLE) hInst;
+  openFileName.lpstrFilter       = nameFilt;
+  openFileName.lpstrCustomFilter = (LPSTR) NULL;
+  openFileName.nMaxCustFilter    = 0L;
+  openFileName.nFilterIndex      = 1L;
+  openFileName.lpstrFile         = fileName;
+  openFileName.nMaxFile          = MSG_SIZ;
+  openFileName.lpstrFileTitle    = fileTitle;
+  openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;
+  openFileName.lpstrInitialDir   = NULL;
+  openFileName.lpstrTitle        = dlgTitle;
+  openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY 
+    | (write ? 0 : OFN_FILEMUSTEXIST) 
+    | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)
+    | (oldDialog ? 0 : OFN_EXPLORER);
+  openFileName.nFileOffset       = 0;
+  openFileName.nFileExtension    = 0;
+  openFileName.lpstrDefExt       = defExt;
+  openFileName.lCustData         = (LONG) number;
+  openFileName.lpfnHook          = oldDialog ?
+    (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;
+  openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);
+
+  if (write ? GetSaveFileName(&openFileName) : 
+              GetOpenFileName(&openFileName)) {
+    /* open the file */
+    f = fopen(openFileName.lpstrFile, write ? "a" : "r");
+    if (f == NULL) {
+      MessageBox(hwnd, "File open failed", NULL,
+                MB_OK|MB_ICONEXCLAMATION);
+      return NULL;
+    }
+  } else {
+    int err = CommDlgExtendedError();
+    if (err != 0) DisplayError("Internal error in file dialog box", err);
+    return FALSE;
+  }
+  return f;
+}
+
+
+
+VOID APIENTRY
+MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)
+{
+  HMENU hmenuTrackPopup;       /* floating pop-up menu  */
+
+  /*
+   * Get the first pop-up menu in the menu template. This is the
+   * menu that TrackPopupMenu displays.
+   */
+  hmenuTrackPopup = GetSubMenu(hmenu, 0);
+
+  SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);
+
+  /*
+   * TrackPopup uses screen coordinates, so convert the
+   * coordinates of the mouse click to screen coordinates.
+   */
+  ClientToScreen(hwnd, (LPPOINT) &pt);
+
+  /* Draw and track the floating pop-up menu. */
+  TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,
+                pt.x, pt.y, 0, hwnd, NULL);
+
+  /* Destroy the menu.*/
+  DestroyMenu(hmenu);
+}
+   
+typedef struct {
+  HWND hDlg, hText;
+  int sizeX, sizeY, newSizeX, newSizeY;
+  HDWP hdwp;
+} ResizeEditPlusButtonsClosure;
+
+BOOL CALLBACK
+ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)
+{
+  ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;
+  RECT rect;
+  POINT pt;
+
+  if (hChild == cl->hText) return TRUE;
+  GetWindowRect(hChild, &rect); /* gives screen coords */
+  pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;
+  pt.y = rect.top + cl->newSizeY - cl->sizeY;
+  ScreenToClient(cl->hDlg, &pt);
+  cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, 
+    pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
+  return TRUE;
+}
+
+/* Resize a dialog that has a (rich) edit field filling most of
+   the top, with a row of buttons below */
+VOID
+ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)
+{
+  RECT rectText;
+  int newTextHeight, newTextWidth;
+  ResizeEditPlusButtonsClosure cl;
+  
+  /*if (IsIconic(hDlg)) return;*/
+  if (newSizeX == sizeX && newSizeY == sizeY) return;
+  
+  cl.hdwp = BeginDeferWindowPos(8);
+
+  GetWindowRect(hText, &rectText); /* gives screen coords */
+  newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
+  newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
+  if (newTextHeight < 0) {
+    newSizeY += -newTextHeight;
+    newTextHeight = 0;
+  }
+  cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, 
+    newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
+
+  cl.hDlg = hDlg;
+  cl.hText = hText;
+  cl.sizeX = sizeX;
+  cl.sizeY = sizeY;
+  cl.newSizeX = newSizeX;
+  cl.newSizeY = newSizeY;
+  EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);
+
+  EndDeferWindowPos(cl.hdwp);
+}
+
+/* Center one window over another */
+BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
+{
+    RECT    rChild, rParent;
+    int     wChild, hChild, wParent, hParent;
+    int     wScreen, hScreen, xNew, yNew;
+    HDC     hdc;
+
+    /* Get the Height and Width of the child window */
+    GetWindowRect (hwndChild, &rChild);
+    wChild = rChild.right - rChild.left;
+    hChild = rChild.bottom - rChild.top;
+
+    /* Get the Height and Width of the parent window */
+    GetWindowRect (hwndParent, &rParent);
+    wParent = rParent.right - rParent.left;
+    hParent = rParent.bottom - rParent.top;
+
+    /* Get the display limits */
+    hdc = GetDC (hwndChild);
+    wScreen = GetDeviceCaps (hdc, HORZRES);
+    hScreen = GetDeviceCaps (hdc, VERTRES);
+    ReleaseDC(hwndChild, hdc);
+
+    /* Calculate new X position, then adjust for screen */
+    xNew = rParent.left + ((wParent - wChild) /2);
+    if (xNew < 0) {
+       xNew = 0;
+    } else if ((xNew+wChild) > wScreen) {
+       xNew = wScreen - wChild;
+    }
+
+    /* Calculate new Y position, then adjust for screen */
+    yNew = rParent.top  + ((hParent - hChild) /2);
+    if (yNew < 0) {
+       yNew = 0;
+    } else if ((yNew+hChild) > hScreen) {
+       yNew = hScreen - hChild;
+    }
+
+    /* Set it, and return */
+    return SetWindowPos (hwndChild, NULL,
+                        xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ * Startup Dialog functions
+ *
+\*---------------------------------------------------------------------------*/
+void
+InitComboStrings(HANDLE hwndCombo, char **cd)
+{
+  SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
+
+  while (*cd != NULL) {
+    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);
+    cd++;
+  }
+}
+
+void
+InitComboStringsFromOption(HANDLE hwndCombo, char *str)
+{
+  char buf1[ARG_MAX];
+  int len;
+
+  if (str[0] == '@') {
+    FILE* f = fopen(str + 1, "r");
+    if (f == NULL) {
+      DisplayFatalError(str + 1, errno, 2);
+      return;
+    }
+    len = fread(buf1, 1, sizeof(buf1)-1, f);
+    fclose(f);
+    buf1[len] = NULLCHAR;
+    str = buf1;
+  }
+
+  SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
+
+  for (;;) {
+    char buf[MSG_SIZ];
+    char *end = strchr(str, '\n');
+    if (end == NULL) return;
+    memcpy(buf, str, end - str);
+    buf[end - str] = NULLCHAR;
+    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);
+    str = end + 1;
+  }
+}
+
+void
+SetStartupDialogEnables(HWND hDlg)
+{
+  EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),
+    IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
+    appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer));
+  EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),
+    IsDlgButtonChecked(hDlg, OPT_ChessEngine));
+  EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),
+    IsDlgButtonChecked(hDlg, OPT_ChessServer));
+  EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),
+    IsDlgButtonChecked(hDlg, OPT_AnyAdditional));
+  EnableWindow(GetDlgItem(hDlg, IDOK),
+    IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||
+    IsDlgButtonChecked(hDlg, OPT_ChessServer) ||
+    IsDlgButtonChecked(hDlg, OPT_View));
+}
+
+char *
+QuoteForFilename(char *filename)
+{
+  int dquote, space;
+  dquote = strchr(filename, '"') != NULL;
+  space = strchr(filename, ' ') != NULL;
+  if (dquote || space) {
+    if (dquote) {
+      return "'";
+    } else {
+      return "\"";
+    }
+  } else {
+    return "";
+  }
+}
+
+VOID
+InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)
+{
+  char buf[MSG_SIZ];
+  char *q;
+
+  InitComboStringsFromOption(hwndCombo, nthnames);
+  q = QuoteForFilename(nthcp);
+  sprintf(buf, "%s%s%s", q, nthcp, q);
+  if (*nthdir != NULLCHAR) {
+    q = QuoteForFilename(nthdir);
+    sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);
+  }
+  if (*nthcp == NULLCHAR) {
+    SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
+  } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
+    SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
+    SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
+  }
+}
+
+LRESULT CALLBACK
+StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  char buf[MSG_SIZ];
+  HANDLE hwndCombo;
+  char *p;
+
+  switch (message) {
+  case WM_INITDIALOG:
+    /* Center the dialog */
+    CenterWindow (hDlg, GetDesktopWindow());
+    /* Initialize the dialog items */
+    InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),
+                 appData.firstChessProgram, "fd", appData.firstDirectory,
+                 firstChessProgramNames);
+    InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),
+                 appData.secondChessProgram, "sd", appData.secondDirectory,
+                 secondChessProgramNames);
+    hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);
+    InitComboStringsFromOption(hwndCombo, icsNames);    
+    sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);
+    if (*appData.icsHelper != NULLCHAR) {
+      char *q = QuoteForFilename(appData.icsHelper);
+      sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);
+    }
+    if (*appData.icsHost == NULLCHAR) {
+      SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);
+      /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */
+    } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {
+      SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);
+      SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);
+    }
+    if (chessProgram) {
+      CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);
+    } else if (appData.icsActive) {
+      CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);
+    } else if (appData.noChessProgram) {
+      CheckDlgButton(hDlg, OPT_View, BST_CHECKED);
+    }
+    SetStartupDialogEnables(hDlg);
+    return TRUE;
+
+  case WM_COMMAND:
+    switch (LOWORD(wParam)) {
+    case IDOK:
+      if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {
+        strcpy(buf, "/fcp=");
+       GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
+        p = buf;
+       ParseArgs(StringGet, &p);
+        strcpy(buf, "/scp=");
+       GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
+        p = buf;
+       ParseArgs(StringGet, &p);
+       appData.noChessProgram = FALSE;
+       appData.icsActive = FALSE;
+      } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {
+        strcpy(buf, "/ics /icshost=");
+       GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));
+        p = buf;
+       ParseArgs(StringGet, &p);
+       if (appData.zippyPlay) {
+         strcpy(buf, "/fcp=");
+         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));
+         p = buf;
+         ParseArgs(StringGet, &p);
+       }
+      } else if (IsDlgButtonChecked(hDlg, OPT_View)) {
+       appData.noChessProgram = TRUE;
+       appData.icsActive = FALSE;
+      } else {
+       MessageBox(hDlg, "Choose an option, or cancel to exit",
+                  "Option Error", MB_OK|MB_ICONEXCLAMATION);
+       return TRUE;
+      }
+      if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {
+       GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));
+       p = buf;
+       ParseArgs(StringGet, &p);
+      }
+      EndDialog(hDlg, TRUE);
+      return TRUE;
+
+    case IDCANCEL:
+      ExitEvent(0);
+      return TRUE;
+
+    case IDM_HELPCONTENTS:
+      if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {
+       MessageBox (GetFocus(),
+                   "Unable to activate help",
+                   szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
+      }
+      break;
+
+    default:
+      SetStartupDialogEnables(hDlg);
+      break;
+    }
+    break;
+  }
+  return FALSE;
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ * About box dialog functions
+ *
+\*---------------------------------------------------------------------------*/
+
+/* Process messages for "About" dialog box */
+LRESULT CALLBACK
+About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  switch (message) {
+  case WM_INITDIALOG: /* message: initialize dialog box */
+    /* Center the dialog over the application window */
+    CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+    SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);
+    return (TRUE);
+
+  case WM_COMMAND: /* message: received a command */
+    if (LOWORD(wParam) == IDOK /* "OK" box selected? */
+       || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */
+      EndDialog(hDlg, TRUE); /* Exit the dialog */
+      return (TRUE);
+    }
+    break;
+  }
+  return (FALSE);
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ * Comment Dialog functions
+ *
+\*---------------------------------------------------------------------------*/
+
+LRESULT CALLBACK
+CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  static HANDLE hwndText = NULL;
+  int len, newSizeX, newSizeY, flags;
+  static int sizeX, sizeY;
+  char *str;
+  RECT rect;
+  MINMAXINFO *mmi;
+
+  switch (message) {
+  case WM_INITDIALOG: /* message: initialize dialog box */
+    /* Initialize the dialog items */
+    hwndText = GetDlgItem(hDlg, OPT_CommentText);
+    SetDlgItemText(hDlg, OPT_CommentText, commentText);
+    EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);
+    EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);
+    EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);
+    SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);
+    SetWindowText(hDlg, commentTitle);
+    if (editComment) {
+      SetFocus(hwndText);
+    } else {
+      SetFocus(GetDlgItem(hDlg, IDOK));
+    }
+    SendMessage(GetDlgItem(hDlg, OPT_CommentText),
+               WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,
+               MAKELPARAM(FALSE, 0));
+    /* Size and position the dialog */
+    if (!commentDialog) {
+      commentDialog = hDlg;
+      flags = SWP_NOZORDER;
+      GetClientRect(hDlg, &rect);
+      sizeX = rect.right;
+      sizeY = rect.bottom;
+      if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&
+         commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {
+       WINDOWPLACEMENT wp;
+       EnsureOnScreen(&commentX, &commentY);
+       wp.length = sizeof(WINDOWPLACEMENT);
+       wp.flags = 0;
+       wp.showCmd = SW_SHOW;
+       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
+       wp.rcNormalPosition.left = commentX;
+       wp.rcNormalPosition.right = commentX + commentW;
+       wp.rcNormalPosition.top = commentY;
+       wp.rcNormalPosition.bottom = commentY + commentH;
+       SetWindowPlacement(hDlg, &wp);
+
+       GetClientRect(hDlg, &rect);
+       newSizeX = rect.right;
+       newSizeY = rect.bottom;
+        ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,
+                             newSizeX, newSizeY);
+       sizeX = newSizeX;
+       sizeY = newSizeY;
+      }
+    }
+    return FALSE;
+
+  case WM_COMMAND: /* message: received a command */
+    switch (LOWORD(wParam)) {
+    case IDOK:
+      if (editComment) {
+       char *p, *q;
+       /* Read changed options from the dialog box */
+       hwndText = GetDlgItem(hDlg, OPT_CommentText);
+       len = GetWindowTextLength(hwndText);
+       str = (char *) malloc(len + 1);
+       GetWindowText(hwndText, str, len + 1);
+       p = q = str;
+       while (*q) {
+         if (*q == '\r')
+           q++;
+         else
+           *p++ = *q++;
+       }
+       *p = NULLCHAR;
+       ReplaceComment(commentIndex, str);
+       free(str);
+      }
+      CommentPopDown();
+      return TRUE;
+
+    case IDCANCEL:
+    case OPT_CancelComment:
+      CommentPopDown();
+      return TRUE;
+
+    case OPT_ClearComment:
+      SetDlgItemText(hDlg, OPT_CommentText, "");
+      break;
+
+    case OPT_EditComment:
+      EditCommentEvent();
+      return TRUE;
+
+    default:
+      break;
+    }
+    break;
+
+  case WM_SIZE:
+    newSizeX = LOWORD(lParam);
+    newSizeY = HIWORD(lParam);
+    ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);
+    sizeX = newSizeX;
+    sizeY = newSizeY;
+    break;
+
+  case WM_GETMINMAXINFO:
+    /* Prevent resizing window too small */
+    mmi = (MINMAXINFO *) lParam;
+    mmi->ptMinTrackSize.x = 100;
+    mmi->ptMinTrackSize.y = 100;
+    break;
+  }
+  return FALSE;
+}
+
+VOID
+EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)
+{
+  FARPROC lpProc;
+  char *p, *q;
+
+  CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);
+
+  if (str == NULL) str = "";
+  p = (char *) malloc(2 * strlen(str) + 2);
+  q = p;
+  while (*str) {
+    if (*str == '\n') *q++ = '\r';
+    *q++ = *str++;
+  }
+  *q = NULLCHAR;
+  if (commentText != NULL) free(commentText);
+
+  commentIndex = index;
+  commentTitle = title;
+  commentText = p;
+  editComment = edit;
+
+  if (commentDialog) {
+    SendMessage(commentDialog, WM_INITDIALOG, 0, 0);
+    if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);
+  } else {
+    lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);
+    CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),
+                hwndMain, (DLGPROC)lpProc);
+    FreeProcInstance(lpProc);
+  }
+  commentDialogUp = TRUE;
+}
+
+
+/*---------------------------------------------------------------------------*\
+ *
+ * Type-in move dialog functions
+ * 
+\*---------------------------------------------------------------------------*/
+
+LRESULT CALLBACK
+TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  char move[MSG_SIZ];
+  HWND hInput;
+  ChessMove moveType;
+  int fromX, fromY, toX, toY;
+  char promoChar;
+
+  switch (message) {
+  case WM_INITDIALOG:
+    move[0] = (char) lParam;
+    move[1] = NULLCHAR;
+    CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
+    hInput = GetDlgItem(hDlg, OPT_Move);
+    SetWindowText(hInput, move);
+    SetFocus(hInput);
+    SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);
+    return FALSE;
+
+  case WM_COMMAND:
+    switch (LOWORD(wParam)) {
+    case IDOK:
+      if (gameMode != EditGame && currentMove != forwardMostMove && 
+       gameMode != Training) {
+       DisplayMoveError("Displayed move is not current");
+      } else {
+       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));
+       if (ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, 
+         &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
+         if (gameMode != Training)
+             forwardMostMove = currentMove;
+         UserMoveEvent(fromX, fromY, toX, toY, promoChar);     
+       } else {
+         DisplayMoveError("Could not parse move");
+       }
+      }
+      EndDialog(hDlg, TRUE);
+      return TRUE;
+    case IDCANCEL:
+      EndDialog(hDlg, FALSE);
+      return TRUE;
+    default:
+      break;
+    }
+    break;
+  }
+  return FALSE;
+}
+
+VOID
+PopUpMoveDialog(char firstchar)
+{
+    FARPROC lpProc;
+    
+    if ((gameMode == BeginningOfGame && !appData.icsActive) || 
+        gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
+       gameMode == AnalyzeMode || gameMode == EditGame || 
+       gameMode == EditPosition || gameMode == IcsExamining ||
+       gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
+       gameMode == Training) {
+      lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);
+      DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),
+       hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);
+      FreeProcInstance(lpProc);
+    }
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ *  Error dialogs
+ * 
+\*---------------------------------------------------------------------------*/
+
+/* Nonmodal error box */
+VOID
+ErrorPopDown()
+{
+  if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");
+  if (errorDialog == NULL) return;
+  DestroyWindow(errorDialog);
+  errorDialog = NULL;
+}
+
+LRESULT CALLBACK
+ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  HANDLE hwndText;
+  RECT rChild;
+
+  switch (message) {
+  case WM_INITDIALOG:
+    GetWindowRect(hDlg, &rChild);
+    SetWindowPos(hDlg, NULL, rChild.left,
+      rChild.top + boardRect.top - (rChild.bottom - rChild.top), 
+      0, 0, SWP_NOZORDER|SWP_NOSIZE);
+    errorDialog = hDlg;
+    hwndText = GetDlgItem(hDlg, OPT_ErrorText);
+    SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);
+    return FALSE;
+
+  case WM_COMMAND:
+    switch (LOWORD(wParam)) {
+    case IDOK:
+    case IDCANCEL:
+      if (errorDialog = hDlg) errorDialog = NULL;
+      DestroyWindow(hDlg);
+      return TRUE;
+
+    default:
+      break;
+    }
+    break;
+  }
+  return FALSE;
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ *  Ics Interaction console functions
+ *
+\*---------------------------------------------------------------------------*/
+
+#define HISTORY_SIZE 64
+static char *history[HISTORY_SIZE];
+int histIn = 0, histP = 0;
+
+VOID
+SaveInHistory(char *cmd)
+{
+  if (history[histIn] != NULL) {
+    free(history[histIn]);
+    history[histIn] = NULL;
+  }
+  if (*cmd == NULLCHAR) return;
+  history[histIn] = StrSave(cmd);
+  histIn = (histIn + 1) % HISTORY_SIZE;
+  if (history[histIn] != NULL) {
+    free(history[histIn]);
+    history[histIn] = NULL;
+  }
+  histP = histIn;
+}
+
+char *
+PrevInHistory(char *cmd)
+{
+  int newhp;
+  if (histP == histIn) {
+    if (history[histIn] != NULL) free(history[histIn]);
+    history[histIn] = StrSave(cmd);
+  }
+  newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
+  if (newhp == histIn || history[newhp] == NULL) return NULL;
+  histP = newhp;
+  return history[histP];
+}
+
+char *
+NextInHistory()
+{
+  if (histP == histIn) return NULL;
+  histP = (histP + 1) % HISTORY_SIZE;
+  return history[histP];
+}
+
+typedef struct {
+  char *item;
+  char *command;
+  BOOLEAN getname;
+  BOOLEAN immediate;
+} IcsTextMenuEntry;
+#define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)
+IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];
+
+void
+ParseIcsTextMenu(char *icsTextMenuString)
+{
+  int flags = 0;
+  IcsTextMenuEntry *e = icsTextMenuEntry;
+  char *p = icsTextMenuString;
+  while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
+    free(e->item);
+    e->item = NULL;
+    if (e->command != NULL) {
+      free(e->command);
+      e->command = NULL;
+    }
+    e++;
+  }
+  e = icsTextMenuEntry;
+  while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {
+    if (*p == ';' || *p == '\n') {
+      e->item = strdup("-");
+      e->command = NULL;
+      p++;
+    } else if (*p == '-') {
+      e->item = strdup("-");
+      e->command = NULL;
+      p++;
+      if (*p) p++;
+    } else {
+      char *q, *r, *s, *t;
+      char c;
+      q = strchr(p, ',');
+      if (q == NULL) break;
+      *q = NULLCHAR;
+      r = strchr(q + 1, ',');
+      if (r == NULL) break;
+      *r = NULLCHAR;
+      s = strchr(r + 1, ',');
+      if (s == NULL) break;
+      *s = NULLCHAR;
+      c = ';';
+      t = strchr(s + 1, c);
+      if (t == NULL) {
+       c = '\n';
+       t = strchr(s + 1, c);
+      }
+      if (t != NULL) *t = NULLCHAR;
+      e->item = strdup(p);
+      e->command = strdup(q + 1);
+      e->getname = *(r + 1) != '0';
+      e->immediate = *(s + 1) != '0';
+      *q = ',';
+      *r = ',';
+      *s = ',';
+      if (t == NULL) break;
+      *t = c;
+      p = t + 1;
+    }
+    e++;
+  } 
+}
+
+HMENU
+LoadIcsTextMenu(IcsTextMenuEntry *e)
+{
+  HMENU hmenu, h;
+  int i = 0;
+  hmenu = LoadMenu(hInst, "TextMenu");
+  h = GetSubMenu(hmenu, 0);
+  while (e->item) {
+    if (strcmp(e->item, "-") == 0) {
+      AppendMenu(h, MF_SEPARATOR, 0, 0);
+    } else {
+      if (e->item[0] == '|') {
+       AppendMenu(h, MF_STRING|MF_MENUBARBREAK,
+                  IDM_CommandX + i, &e->item[1]);
+      } else {
+       AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);
+      }
+    }
+    e++;
+    i++;
+  } 
+  return hmenu;
+}
+
+WNDPROC consoleTextWindowProc;
+WNDPROC PvWindowProc;
+WNDPROC NameWindowProc;
+
+void
+CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
+{
+  char buf[MSG_SIZ], name[MSG_SIZ];
+  HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+  CHARRANGE sel;
+
+  if (!getname) {
+    SetWindowText(hInput, command);
+    if (immediate) {
+      SendMessage(hInput, WM_CHAR, '\r', 0);
+    } else {
+      sel.cpMin = 999999;
+      sel.cpMax = 999999;
+      SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
+      SetFocus(hInput);
+    }
+    return;
+  }    
+  SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+  if (sel.cpMin == sel.cpMax) {
+    /* Expand to surrounding word */
+    TEXTRANGE tr;
+    do {
+      tr.chrg.cpMax = sel.cpMin;
+      tr.chrg.cpMin = --sel.cpMin;
+      if (sel.cpMin < 0) break;
+      tr.lpstrText = name;
+      SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
+    } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
+    sel.cpMin++;
+
+    do {
+      tr.chrg.cpMin = sel.cpMax;
+      tr.chrg.cpMax = ++sel.cpMax;
+      tr.lpstrText = name;
+      if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;
+    } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');
+    sel.cpMax--;
+
+    if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {
+      MessageBeep(MB_ICONEXCLAMATION);
+      return;
+    }
+    tr.chrg = sel;
+    tr.lpstrText = name;
+    SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);
+  } else {
+    if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {
+      MessageBeep(MB_ICONEXCLAMATION);
+      return;
+    }
+    SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);
+  }
+  if (immediate) {
+       sprintf(buf, "%s %s", command, name);
+       SetWindowText(hInput, buf);
+       SendMessage(hInput, WM_CHAR, '\r', 0);
+  } else {
+    sprintf(buf, "%s %s ", command, name); /* trailing space */
+    SetWindowText(hInput, buf);
+    sel.cpMin = 999999;
+    sel.cpMax = 999999;
+    SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);
+    SetFocus(hInput);
+  }
+}
+
+LRESULT CALLBACK 
+ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  HWND hInput;
+  CHARRANGE sel;
+  switch (message) {
+  case WM_KEYDOWN:
+    if (!(GetKeyState(VK_CONTROL) & ~1)) break;
+    switch (wParam) {
+    case VK_PRIOR:
+      SendMessage(hwnd, EM_LINESCROLL, 0, -999999);
+      return 0;
+    case VK_NEXT:
+      sel.cpMin = 999999;
+      sel.cpMax = 999999;
+      SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
+      SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
+      return 0;
+    }
+    break;
+  case WM_CHAR:
+    if (wParam == '\t') {
+      if (GetKeyState(VK_SHIFT) < 0) {
+       /* shifted */
+       if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
+       if (buttonDesc[0].hwnd) {
+         SetFocus(buttonDesc[0].hwnd);
+       } else {
+         SetFocus(hwndMain);
+       }
+      } else {
+       /* unshifted */
+       SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
+      }
+    } else {
+      hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+      SetFocus(hInput);
+      SendMessage(hInput, message, wParam, lParam);
+    }
+    return 0;
+  case WM_PASTE:
+    hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+    SetFocus(hInput);
+    return SendMessage(hInput, message, wParam, lParam);
+  case WM_MBUTTONDOWN:
+    return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
+  case WM_RBUTTONDOWN:
+    if (!(GetKeyState(VK_SHIFT) & ~1)) {
+      /* Move selection here if it was empty */
+      POINT pt;
+      pt.x = LOWORD(lParam);
+      pt.y = HIWORD(lParam);
+      SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+      if (sel.cpMin == sel.cpMax) {
+        sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/
+       sel.cpMax = sel.cpMin;
+       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
+      }
+      SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);
+    }
+    return 0;
+  case WM_RBUTTONUP:
+    if (GetKeyState(VK_SHIFT) & ~1) {
+      SendDlgItemMessage(hwndConsole, OPT_ConsoleText, 
+        WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
+    } else {
+      POINT pt;
+      HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);
+      SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel); 
+      if (!IsClipboardFormatAvailable(CF_TEXT)) {
+        EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
+      }
+      pt.x = LOWORD(lParam);
+      pt.y = HIWORD(lParam);
+      MenuPopup(hwnd, pt, hmenu, -1);
+    }
+    return 0;
+  case WM_COMMAND:
+    switch (LOWORD(wParam)) {
+    case IDM_QuickPaste:
+      {
+        SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+       if (sel.cpMin == sel.cpMax) {
+         MessageBeep(MB_ICONEXCLAMATION);
+          return 0;
+       }
+       SendMessage(hwnd, WM_COPY, 0, 0);
+       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+        SendMessage(hInput, WM_PASTE, 0, 0);
+        SetFocus(hInput);
+        return 0;
+      }
+    case IDM_Cut:
+      SendMessage(hwnd, WM_CUT, 0, 0);
+      return 0;
+    case IDM_Paste:
+      SendMessage(hwnd, WM_PASTE, 0, 0);
+      return 0;
+    case IDM_Copy:
+      SendMessage(hwnd, WM_COPY, 0, 0);
+      return 0;
+    default:
+      {
+       int i = LOWORD(wParam) - IDM_CommandX;
+       if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&
+           icsTextMenuEntry[i].command != NULL) {
+         CommandX(hwnd, icsTextMenuEntry[i].command,
+                  icsTextMenuEntry[i].getname,
+                  icsTextMenuEntry[i].immediate);
+         return 0;
+       }
+      }
+      break;
+    }
+    break;
+  }
+  return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);
+}
+
+WNDPROC consoleInputWindowProc;
+
+LRESULT CALLBACK
+ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  char buf[MSG_SIZ];
+  char *p;
+  static BOOL sendNextChar = FALSE;
+  static BOOL quoteNextChar = FALSE;
+  InputSource *is = consoleInputSource;
+  CHARFORMAT cf;
+  CHARRANGE sel;
+
+  switch (message) {
+  case WM_CHAR:
+    if (!appData.localLineEditing || sendNextChar) {
+      is->buf[0] = (CHAR) wParam;
+      is->count = 1;
+      SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
+      sendNextChar = FALSE;
+      return 0;
+    }
+    if (quoteNextChar) {
+      buf[0] = (char) wParam;
+      buf[1] = NULLCHAR;
+      SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);
+      quoteNextChar = FALSE;
+      return 0;
+    }
+    switch (wParam) {
+    case '\r':   /* Enter key */
+      is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     
+      if (consoleEcho) SaveInHistory(is->buf);
+      is->buf[is->count++] = '\n';
+      is->buf[is->count] = NULLCHAR;
+      SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
+      if (consoleEcho) {
+       ConsoleOutput(is->buf, is->count, TRUE);
+      } else if (appData.localLineEditing) {
+       ConsoleOutput("\n", 1, TRUE);
+      }
+      /* fall thru */
+    case '\033': /* Escape key */
+      SetWindowText(hwnd, "");
+      cf.cbSize = sizeof(CHARFORMAT);
+      cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
+      if (consoleEcho) {
+        cf.crTextColor = textAttribs[ColorNormal].color;
+      } else {
+       cf.crTextColor = COLOR_ECHOOFF;
+      }
+      cf.dwEffects = textAttribs[ColorNormal].effects;
+      SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
+      return 0;
+    case '\t':   /* Tab key */
+      if (GetKeyState(VK_SHIFT) < 0) {
+       /* shifted */
+       SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));
+      } else {
+       /* unshifted */
+       if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);
+       if (buttonDesc[0].hwnd) {
+         SetFocus(buttonDesc[0].hwnd);
+       } else {
+         SetFocus(hwndMain);
+       }
+      }
+      return 0;
+    case '\023': /* Ctrl+S */
+      sendNextChar = TRUE;
+      return 0;
+    case '\021': /* Ctrl+Q */
+      quoteNextChar = TRUE;
+      return 0;
+    default:
+      break;
+    }
+    break;
+  case WM_KEYDOWN:
+    switch (wParam) {
+    case VK_UP:
+      GetWindowText(hwnd, buf, MSG_SIZ);
+      p = PrevInHistory(buf);
+      if (p != NULL) {
+       SetWindowText(hwnd, p);
+       sel.cpMin = 999999;
+       sel.cpMax = 999999;
+       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
+        return 0;
+      }
+      break;
+    case VK_DOWN:
+      p = NextInHistory();
+      if (p != NULL) {
+       SetWindowText(hwnd, p);
+       sel.cpMin = 999999;
+       sel.cpMax = 999999;
+       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
+        return 0;
+      }
+      break;
+    case VK_HOME:
+    case VK_END:
+      if (!(GetKeyState(VK_CONTROL) & ~1)) break;
+      /* fall thru */
+    case VK_PRIOR:
+    case VK_NEXT:
+      SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);
+      return 0;
+    }
+    break;
+  case WM_MBUTTONDOWN:
+    SendDlgItemMessage(hwndConsole, OPT_ConsoleText, 
+      WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
+    break;
+  case WM_RBUTTONUP:
+    if (GetKeyState(VK_SHIFT) & ~1) {
+      SendDlgItemMessage(hwndConsole, OPT_ConsoleText, 
+        WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);
+    } else {
+      POINT pt;
+      HMENU hmenu;
+      hmenu = LoadMenu(hInst, "InputMenu");
+      SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);
+      if (sel.cpMin == sel.cpMax) {
+        EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);
+        EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);
+      }
+      if (!IsClipboardFormatAvailable(CF_TEXT)) {
+        EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);
+      }
+      pt.x = LOWORD(lParam);
+      pt.y = HIWORD(lParam);
+      MenuPopup(hwnd, pt, hmenu, -1);
+    }
+    return 0;
+  case WM_COMMAND:
+    switch (LOWORD(wParam)) { 
+    case IDM_Undo:
+      SendMessage(hwnd, EM_UNDO, 0, 0);
+      return 0;
+    case IDM_SelectAll:
+      sel.cpMin = 0;
+      sel.cpMax = -1; /*999999?*/
+      SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
+      return 0;
+    case IDM_Cut:
+      SendMessage(hwnd, WM_CUT, 0, 0);
+      return 0;
+    case IDM_Paste:
+      SendMessage(hwnd, WM_PASTE, 0, 0);
+      return 0;
+    case IDM_Copy:
+      SendMessage(hwnd, WM_COPY, 0, 0);
+      return 0;
+    }
+    break;
+  }
+  return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);
+}
+
+#define CO_MAX  100000
+#define CO_TRIM   1000
+
+LRESULT CALLBACK
+ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  static HWND hText, hInput, hFocus;
+  InputSource *is = consoleInputSource;
+  RECT rect;
+  static int sizeX, sizeY;
+  int newSizeX, newSizeY;
+  MINMAXINFO *mmi;
+
+  switch (message) {
+  case WM_INITDIALOG: /* message: initialize dialog box */
+    hwndConsole = hDlg;
+    hText = GetDlgItem(hDlg, OPT_ConsoleText);
+    hInput = GetDlgItem(hDlg, OPT_ConsoleInput);
+    SetFocus(hInput);
+    consoleTextWindowProc = (WNDPROC)
+      SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);
+    SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
+    consoleInputWindowProc = (WNDPROC)
+      SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);
+    SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
+    Colorize(ColorNormal, TRUE);
+    SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);
+    ChangedConsoleFont();
+    GetClientRect(hDlg, &rect);
+    sizeX = rect.right;
+    sizeY = rect.bottom;
+    if (consoleX != CW_USEDEFAULT && consoleY != CW_USEDEFAULT &&
+       consoleW != CW_USEDEFAULT && consoleH != CW_USEDEFAULT) {
+      WINDOWPLACEMENT wp;
+      EnsureOnScreen(&consoleX, &consoleY);
+      wp.length = sizeof(WINDOWPLACEMENT);
+      wp.flags = 0;
+      wp.showCmd = SW_SHOW;
+      wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
+      wp.rcNormalPosition.left = consoleX;
+      wp.rcNormalPosition.right = consoleX + consoleW;
+      wp.rcNormalPosition.top = consoleY;
+      wp.rcNormalPosition.bottom = consoleY + consoleH;
+      SetWindowPlacement(hDlg, &wp);
+    }
+    return FALSE;
+
+  case WM_SETFOCUS:
+    SetFocus(hInput);
+    return 0;
+
+  case WM_CLOSE:
+    ExitEvent(0);
+    /* not reached */
+    break;
+
+  case WM_SIZE:
+    if (IsIconic(hDlg)) break;
+    newSizeX = LOWORD(lParam);
+    newSizeY = HIWORD(lParam);
+    if (sizeX != newSizeX || sizeY != newSizeY) {
+      RECT rectText, rectInput;
+      POINT pt;
+      int newTextHeight, newTextWidth;
+      GetWindowRect(hText, &rectText);
+      newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;
+      newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;
+      if (newTextHeight < 0) {
+       newSizeY += -newTextHeight;
+        newTextHeight = 0;
+      }
+      SetWindowPos(hText, NULL, 0, 0,
+       newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
+      GetWindowRect(hInput, &rectInput); /* gives screen coords */
+      pt.x = rectInput.left;
+      pt.y = rectInput.top + newSizeY - sizeY;
+      ScreenToClient(hDlg, &pt);
+      SetWindowPos(hInput, NULL, 
+       pt.x, pt.y, /* needs client coords */   
+       rectInput.right - rectInput.left + newSizeX - sizeX,
+       rectInput.bottom - rectInput.top, SWP_NOZORDER);
+    }
+    sizeX = newSizeX;
+    sizeY = newSizeY;
+    break;
+
+  case WM_GETMINMAXINFO:
+    /* Prevent resizing window too small */
+    mmi = (MINMAXINFO *) lParam;
+    mmi->ptMinTrackSize.x = 100;
+    mmi->ptMinTrackSize.y = 100;
+    break;
+  }
+  return DefWindowProc(hDlg, message, wParam, lParam);
+}
+
+
+VOID
+ConsoleCreate()
+{
+  HWND hCons;
+  if (hwndConsole) return;
+  hCons = CreateDialog(hInst, szConsoleName, 0, NULL);
+  SendMessage(hCons, WM_INITDIALOG, 0, 0);
+}
+
+BOOL httpseglight_continued = FALSE;
+char *spitlight(HWND hText, char *p, ColorClass cc, CHARRANGE *sel) {
+       int effects;
+       char *urlstart = p;
+       char backup;
+       httpseglight_continued = TRUE;
+       backup = 0;
+       while (*p) {
+               switch(*p) {
+                       case '\n': case '\r': case ' ': case '\t': 
+                               backup = *p; *p = 0; httpseglight_continued = FALSE; break;
+                       default: p++;
+                       }
+       }
+    effects = consoleCF.dwEffects;
+    consoleCF.crTextColor = textAttribs[ColorKibitz].color;
+    consoleCF.dwEffects |= CFM_UNDERLINE;
+       SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)sel);
+       SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
+    SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) urlstart);
+       sel->cpMax = sel->cpMin += p - urlstart;
+    consoleCF.crTextColor = textAttribs[currentColorClass].color;
+    consoleCF.dwEffects = effects;
+    SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)sel);
+       SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
+       *p = backup;
+       return p;
+}
+char *strnpstr(char *p, char *needle, char *end) {
+       char *loc;
+       char *o = needle;
+       while (p < end) {
+               if (*p == *o) {
+                       if (o==needle) loc = p;
+                       o++;
+                       } else o = needle;
+               if (*o == 0) return loc;
+               p++;
+               }
+               return p=NULL;
+       }
+int seglight(HWND hText, char *p, char *end, char *seg, int seglen, ColorClass cc, CHARRANGE *sel) {
+    char *op, *urlstart;
+    if (p==NULL || seg == NULL || sel == NULL) return -1;
+       if (httpseglight_continued) {
+         p = spitlight(hText, p, cc, sel);
+       }
+    while (end-p >= seglen && (urlstart = strnpstr(p, seg, end)) != NULL) {
+               op = urlstart;
+               *op = 0;
+               SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) p);
+               sel->cpMax = sel->cpMin += op - p;
+               *op = seg[0];
+               p = spitlight(hText, op, cc, sel);
+       }
+       if (end-p>0) {
+               SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) p);
+               sel->cpMax = sel->cpMin += end-p;
+       }
+       return 0;       
+}
+VOID
+ConsoleOutput(char* data, int length, int forceVisible)
+{
+  HWND hText;
+  int trim, exlen;
+  char *p, *q;
+  char buf[CO_MAX+1];
+  POINT pEnd;
+  RECT rect;
+  static int delayLF = 0;
+  CHARRANGE savesel, sel;
+
+  if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;
+  p = data;
+  q = buf;
+  if (delayLF) {
+    *q++ = '\r';
+    *q++ = '\n';
+    delayLF = 0;
+  }
+  while (length--) {
+    if (*p == '\n') {
+      if (*++p) {
+       *q++ = '\r';
+       *q++ = '\n';
+      } else {
+       delayLF = 1;
+      }
+    } else if (*p == '\007') {
+       MyPlaySound(&sounds[(int)SoundBell]);
+       p++;
+    } else {
+      *q++ = *p++;
+    }
+  }
+  *q = NULLCHAR;
+  hText = GetDlgItem(hwndConsole, OPT_ConsoleText);
+  SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
+  /* Save current selection */
+  SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);
+  exlen = GetWindowTextLength(hText);
+  /* Find out whether current end of text is visible */
+  SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);
+  SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);
+  /* Trim existing text if it's too long */
+  if (exlen + (q - buf) > CO_MAX) {
+    trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);
+    sel.cpMin = 0;
+    sel.cpMax = trim;
+    SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
+    SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");
+    exlen -= trim;
+    savesel.cpMin -= trim;
+    savesel.cpMax -= trim;
+    if (exlen < 0) exlen = 0;
+    if (savesel.cpMin < 0) savesel.cpMin = 0;
+    if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;
+  }
+  /* Append the new text */
+  sel.cpMin = exlen;
+  sel.cpMax = exlen;
+  SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
+  SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);
+
+  /* append text while checking for and highlight links */
+  seglight(hText, buf, q, "http://", 7, ColorKibitz, &sel);
+  
+  if (forceVisible || exlen == 0 ||
+      (rect.left <= pEnd.x && pEnd.x < rect.right &&
+       rect.top <= pEnd.y && pEnd.y < rect.bottom)) {
+    /* Scroll to make new end of text visible if old end of text
+       was visible or new text is an echo of user typein */
+    sel.cpMin = 9999999;
+    sel.cpMax = 9999999;
+    SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
+    SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
+    SendMessage(hText, EM_SCROLLCARET, 0, 0);
+    SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);
+  }
+  if (savesel.cpMax == exlen || forceVisible) {
+    /* Move insert point to new end of text if it was at the old
+       end of text or if the new text is an echo of user typein */
+    sel.cpMin = 9999999;
+    sel.cpMax = 9999999;
+    SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);
+  } else {
+    /* Restore previous selection */
+    SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);
+  }
+  SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);
+}
+
+/*---------*/
+
+
+void
+DisplayAClock(HDC hdc, int timeRemaining, int highlight,
+             RECT *rect, char *color)
+{
+  char buf[100];
+  char *str;
+  COLORREF oldFg, oldBg;
+  HFONT oldFont;
+
+  if (appData.clockMode) {
+    if (tinyLayout)
+      sprintf(buf, "%c %s", color[0], TimeString(timeRemaining));
+    else
+      sprintf(buf, "%s: %s", color, TimeString(timeRemaining));
+    str = buf;
+  } else {
+    str = color;
+  }
+
+  if (highlight) {
+    oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */
+    oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */
+  } else {
+    oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */
+    oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */
+  }
+  oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);
+
+  ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,
+            rect->top, ETO_CLIPPED|ETO_OPAQUE,
+            rect, str, strlen(str), NULL);
+
+  (void) SetTextColor(hdc, oldFg);
+  (void) SetBkColor(hdc, oldBg);
+  (void) SelectObject(hdc, oldFont);
+}
+
+
+int
+DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
+          OVERLAPPED *ovl)
+{
+  int ok, err;
+
+  ResetEvent(ovl->hEvent);
+  ovl->Offset = ovl->OffsetHigh = 0;
+  ok = ReadFile(hFile, buf, count, outCount, ovl);
+  if (ok) {
+    err = NO_ERROR;
+  } else {
+    err = GetLastError();
+    if (err == ERROR_IO_PENDING) {
+      ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
+      if (ok)
+       err = NO_ERROR;
+      else
+       err = GetLastError();
+    }
+  }
+  return err;
+}
+
+int
+DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,
+           OVERLAPPED *ovl)
+{
+  int ok, err;
+
+  ResetEvent(ovl->hEvent);
+  ovl->Offset = ovl->OffsetHigh = 0;
+  ok = WriteFile(hFile, buf, count, outCount, ovl);
+  if (ok) {
+    err = NO_ERROR;
+  } else {
+    err = GetLastError();
+    if (err == ERROR_IO_PENDING) {
+      ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);
+      if (ok)
+       err = NO_ERROR;
+      else
+       err = GetLastError();
+    }
+  }
+  return err;
+}
+
+
+DWORD
+InputThread(LPVOID arg)
+{
+  InputSource *is;
+  OVERLAPPED ovl;
+
+  is = (InputSource *) arg;
+  ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+  ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
+  while (is->hThread != NULL) {
+    is->error = DoReadFile(is->hFile, is->next,
+                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
+                          &is->count, &ovl);
+    if (is->error == NO_ERROR) {
+      is->next += is->count;
+    } else {
+      if (is->error == ERROR_BROKEN_PIPE) {
+       /* Correct for MS brain damage.  EOF reading a pipe is not an error. */
+       is->count = 0;
+      } else {
+       is->count = (DWORD) -1;
+      }
+    }
+    SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
+    if (is->count <= 0) break;  /* Quit on EOF or error */
+  }
+  CloseHandle(ovl.hEvent);
+  CloseHandle(is->hFile);
+  return 0;
+}
+
+
+/* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */
+DWORD
+NonOvlInputThread(LPVOID arg)
+{
+  InputSource *is;
+  char *p, *q;
+  int i;
+  char prev;
+
+  is = (InputSource *) arg;
+  while (is->hThread != NULL) {
+    is->error = ReadFile(is->hFile, is->next,
+                        INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),
+                        &is->count, NULL) ? NO_ERROR : GetLastError();
+    if (is->error == NO_ERROR) {
+      /* Change CRLF to LF */
+      if (is->next > is->buf) {
+       p = is->next - 1;
+       i = is->count + 1;
+      } else {
+       p = is->next;
+       i = is->count;
+      }
+      q = p;
+      prev = NULLCHAR;
+      while (i > 0) {
+       if (prev == '\r' && *p == '\n') {
+         *(q-1) = '\n';
+         is->count--;
+       } else { 
+         *q++ = *p;
+       }
+       prev = *p++;
+       i--;
+      }
+      *q = NULLCHAR;
+      is->next = q;
+    } else {
+      if (is->error == ERROR_BROKEN_PIPE) {
+       /* Correct for MS brain damage.  EOF reading a pipe is not an error. */
+       is->count = 0; 
+      } else {
+       is->count = (DWORD) -1;
+      }
+    }
+    SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
+    if (is->count < 0) break;  /* Quit on error */
+  }
+  CloseHandle(is->hFile);
+  return 0;
+}
+
+DWORD
+SocketInputThread(LPVOID arg)
+{
+  InputSource *is;
+
+  is = (InputSource *) arg;
+  while (is->hThread != NULL) {
+    is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);
+    if ((int)is->count == SOCKET_ERROR) {
+      is->count = (DWORD) -1;
+      is->error = WSAGetLastError();
+    } else {
+      is->error = NO_ERROR;
+      is->next += is->count;
+      if (is->count == 0 && is->second == is) {
+       /* End of file on stderr; quit with no message */
+       break;
+      }
+    }
+    SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);
+    if (is->count <= 0) break;  /* Quit on EOF or error */
+  }
+  return 0;
+}
+
+VOID
+InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  InputSource *is;
+
+  is = (InputSource *) lParam;
+  if (is->lineByLine) {
+    /* Feed in lines one by one */
+    char *p = is->buf;
+    char *q = p;
+    while (q < is->next) {
+      if (*q++ == '\n') {
+       (is->func)(is, is->closure, p, q - p, NO_ERROR);
+       p = q;
+      }
+    }
+    /* Move any partial line to the start of the buffer */
+    q = is->buf;
+    while (p < is->next) {
+      *q++ = *p++;
+    }
+    is->next = q;
+    if (is->error != NO_ERROR || is->count == 0) {
+      /* Notify backend of the error.  Note: If there was a partial
+        line at the end, it is not flushed through. */
+      (is->func)(is, is->closure, is->buf, is->count, is->error);   
+    }
+  } else {
+    /* Feed in the whole chunk of input at once */
+    (is->func)(is, is->closure, is->buf, is->count, is->error);
+    is->next = is->buf;
+  }
+}
+
+/*---------------------------------------------------------------------------*\
+ *
+ *  Menu enables. Used when setting various modes.
+ *
+\*---------------------------------------------------------------------------*/
+
+typedef struct {
+  int item;
+  int flags;
+} Enables;
+
+VOID
+SetMenuEnables(HMENU hmenu, Enables *enab)
+{
+  while (enab->item > 0) {
+    (void) EnableMenuItem(hmenu, enab->item, enab->flags);
+    enab++;
+  }
+}
+
+Enables gnuEnables[] = {
+  { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
+  { -1, -1 }
+};
+
+Enables icsEnables[] = {
+  { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },
+  { -1, -1 }
+};
+
+#ifdef ZIPPY
+Enables zippyEnables[] = {
+  { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_Book, MF_BYCOMMAND|MF_ENABLED },
+  { -1, -1 }
+};
+#endif
+
+Enables ncpEnables[] = {
+  { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },
+  { ACTION_POS, MF_BYPOSITION|MF_GRAYED },
+  { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Book, MF_BYCOMMAND|MF_GRAYED },
+  { -1, -1 }
+};
+
+Enables trainingOnEnables[] = {
+  { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },
+  { -1, -1 }
+};
+
+Enables trainingOffEnables[] = {
+  { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },
+  { -1, -1 }
+};
+
+/* These modify either ncpEnables or gnuEnables */
+Enables cmailEnables[] = {
+  { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },
+  { ACTION_POS, MF_BYPOSITION|MF_ENABLED },
+  { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },
+  { -1, -1 }
+};
+
+Enables machineThinkingEnables[] = {
+  { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },
+  { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },
+  { -1, -1 }
+};
+
+Enables userThinkingEnables[] = {
+  { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },
+  { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },
+  { -1, -1 }
+};
+
+/*---------------------------------------------------------------------------*\
+ *
+ *  Front-end interface functions exported by XBoard.
+ *  Functions appear in same order as prototypes in frontend.h.
+ * 
+\*---------------------------------------------------------------------------*/
+VOID
+ModeHighlight()
+{
+  static UINT prevChecked = 0;
+  static int prevPausing = 0;
+  UINT nowChecked;
+
+  if (pausing != prevPausing) {
+    prevPausing = pausing;
+    (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,
+                        MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));
+    if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");
+  }
+
+  switch (gameMode) {
+  case BeginningOfGame:
+    if (appData.icsActive)
+      nowChecked = IDM_IcsClient;
+    else if (appData.noChessProgram)
+      nowChecked = IDM_EditGame;
+    else
+      nowChecked = IDM_MachineBlack;
+    break;
+  case MachinePlaysBlack:
+    nowChecked = IDM_MachineBlack;
+    break;
+  case MachinePlaysWhite:
+    nowChecked = IDM_MachineWhite;
+    break;
+  case TwoMachinesPlay:
+    nowChecked = IDM_TwoMachines;
+    break;
+  case AnalyzeMode:
+    nowChecked = IDM_AnalysisMode;
+    break;
+  case AnalyzeFile:
+    nowChecked = IDM_AnalyzeFile;
+    break;
+  case EditGame:
+    nowChecked = IDM_EditGame;
+    break;
+  case PlayFromGameFile:
+    nowChecked = IDM_LoadGame;
+    break;
+  case EditPosition:
+    nowChecked = IDM_EditPosition;
+    break;
+  case Training:
+    nowChecked = IDM_Training;
+    break;
+  case IcsPlayingWhite:
+  case IcsPlayingBlack:
+  case IcsObserving:
+  case IcsIdle:
+    nowChecked = IDM_IcsClient;
+    break;
+  default:
+  case EndOfGame:
+    nowChecked = 0;
+    break;
+  }
+  if (prevChecked != 0)
+    (void) CheckMenuItem(GetMenu(hwndMain),
+                        prevChecked, MF_BYCOMMAND|MF_UNCHECKED);
+  if (nowChecked != 0)
+    (void) CheckMenuItem(GetMenu(hwndMain),
+                        nowChecked, MF_BYCOMMAND|MF_CHECKED);
+
+  if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {
+    (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, 
+                         MF_BYCOMMAND|MF_ENABLED);
+  } else {
+    (void) EnableMenuItem(GetMenu(hwndMain), 
+                         IDM_Training, MF_BYCOMMAND|MF_GRAYED);
+  }
+
+  prevChecked = nowChecked;
+}
+
+VOID
+SetICSMode()
+{
+  HMENU hmenu = GetMenu(hwndMain);
+  SetMenuEnables(hmenu, icsEnables);
+  EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,
+    MF_BYPOSITION|MF_ENABLED);
+#ifdef ZIPPY
+  if (appData.zippyPlay) {
+    SetMenuEnables(hmenu, zippyEnables);
+  }
+#endif
+}
+
+VOID
+SetGNUMode()
+{
+  SetMenuEnables(GetMenu(hwndMain), gnuEnables);
+}
+
+VOID
+SetNCPMode()
+{
+  HMENU hmenu = GetMenu(hwndMain);
+  SetMenuEnables(hmenu, ncpEnables);
+  EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,
+    MF_BYPOSITION|MF_GRAYED);
+    DrawMenuBar(hwndMain);
+}
+
+VOID
+SetCmailMode()
+{
+  SetMenuEnables(GetMenu(hwndMain), cmailEnables);
+}
+
+VOID 
+SetTrainingModeOn()
+{
+  int i;
+  SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);
+  for (i = 0; i < N_BUTTONS; i++) {
+    if (buttonDesc[i].hwnd != NULL)
+      EnableWindow(buttonDesc[i].hwnd, FALSE);
+  }
+  CommentPopDown();
+}
+
+VOID SetTrainingModeOff()
+{
+  int i;
+  SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);
+  for (i = 0; i < N_BUTTONS; i++) {
+    if (buttonDesc[i].hwnd != NULL)
+      EnableWindow(buttonDesc[i].hwnd, TRUE);
+  }
+}
+
+
+VOID
+SetUserThinkingEnables()
+{
+  SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);
+}
+
+VOID
+SetMachineThinkingEnables()
+{
+  HMENU hMenu = GetMenu(hwndMain);
+  int flags = MF_BYCOMMAND|MF_ENABLED;
+
+  SetMenuEnables(hMenu, machineThinkingEnables);
+
+  if (gameMode == MachinePlaysBlack) {
+    (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);
+  } else if (gameMode == MachinePlaysWhite) {
+    (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);
+  } else if (gameMode == TwoMachinesPlay) {
+    (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);
+  }
+}
+
+
+VOID
+DisplayTitle(char *str)
+{
+  char title[MSG_SIZ], *host;
+  if (str[0] != NULLCHAR) {
+    strcpy(title, str);
+  } else if (appData.icsActive) {
+    if (appData.icsCommPort[0] != NULLCHAR)
+      host = "ICS";
+    else 
+      host = appData.icsHost;
+    sprintf(title, "%s: %s", szTitle, host);
+  } else if (appData.noChessProgram) {
+    strcpy(title, szTitle);
+  } else {
+    strcpy(title, szTitle);
+    strcat(title, ": ");
+    strcat(title, first.tidy);
+  }
+  SetWindowText(hwndMain, title);
+}
+
+
+VOID
+DisplayMessage(char *str1, char *str2)
+{
+  HDC hdc;
+  HFONT oldFont;
+  int remain = MESSAGE_TEXT_MAX - 1;
+  int len;
+
+  moveErrorMessageUp = FALSE; /* turned on later by caller if needed */
+  messageText[0] = NULLCHAR;
+  if (*str1) {
+    len = strlen(str1);
+    if (len > remain) len = remain;
+    strncpy(messageText, str1, len);
+    messageText[len] = NULLCHAR;
+    remain -= len;
+  }
+  if (*str2 && remain >= 2) {
+    if (*str1) {
+      strcat(messageText, "  ");
+      remain -= 2;
+    }
+    len = strlen(str2);
+    if (len > remain) len = remain;
+    strncat(messageText, str2, len);
+  }
+  messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;
+
+  if (IsIconic(hwndMain)) return;
+  hdc = GetDC(hwndMain);
+  oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);
+  ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,
+            &messageRect, messageText, strlen(messageText), NULL);
+  (void) SelectObject(hdc, oldFont);
+  (void) ReleaseDC(hwndMain, hdc);
+}
+
+VOID
+DisplayError(char *str, int error)
+{
+  FARPROC lpProc;
+  char buf[MSG_SIZ*2], buf2[MSG_SIZ];
+  int len;
+  char *p, *q;
+
+  if (error == 0) {
+    strcpy(buf, str);
+  } else {
+    len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+                       NULL, error, LANG_NEUTRAL,
+                       (LPSTR) buf2, MSG_SIZ, NULL);
+    if (len > 0) {
+      sprintf(buf, "%s:\n%s", str, buf2);
+    } else {
+      ErrorMap *em = errmap;
+      while (em->err != 0 && em->err != error) em++;
+      if (em->err != 0) {
+       sprintf(buf, "%s:\n%s", str, em->msg);
+      } else {
+       sprintf(buf, "%s:\nError code %d", str, error);
+      }
+    }
+  }
+  p = buf;
+  q = errorMessage;
+  while (*p) {
+    if (*p == '\n') {
+      if (hwndMain != NULL /*!!?*/) {
+        *q++ = '\r';
+        *q++ = *p++;
+      } else {
+       *q++ = ' ';
+        p++;
+      }
+    } else {
+      *q++ = *p++;
+    }
+  }
+  *q = NULLCHAR;
+  
+  if (hwndMain == NULL) {
+    MessageBox(NULL, errorMessage, "Error", MB_OK|MB_ICONEXCLAMATION);
+  } else {
+    lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);
+    CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),
+      hwndMain, (DLGPROC)lpProc);
+    FreeProcInstance(lpProc);
+  }
+}
+
+
+VOID
+DisplayMoveError(char *str)
+{
+  fromX = fromY = -1;
+  ClearHighlights();
+  DrawPosition(FALSE, NULL);
+  if (appData.popupMoveErrors) {
+    DisplayError(str, 0);
+  } else {
+    DisplayMessage(str, "");
+    moveErrorMessageUp = TRUE;
+  }
+}
+
+VOID
+DisplayFatalError(char *str, int error, int exitStatus)
+{
+  char buf[2*MSG_SIZ], buf2[MSG_SIZ];
+  int len;
+  char *label = exitStatus ? "Fatal Error" : "Exiting";
+
+  if (error != 0) {
+    len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+                       NULL, error, LANG_NEUTRAL,
+                       (LPSTR) buf2, MSG_SIZ, NULL);
+    if (len > 0) {
+      sprintf(buf, "%s:\n%s", str, buf2);
+    } else {
+      ErrorMap *em = errmap;
+      while (em->err != 0 && em->err != error) em++;
+      if (em->err != 0) {
+       sprintf(buf, "%s:\n%s", str, em->msg);
+      } else {
+       sprintf(buf, "%s:\nError code %d", str, error);
+      }
+    }
+    str = buf;
+  }
+  if (appData.debugMode) {
+    fprintf(debugFP, "%s: %s\n", label, str);
+  }
+  if (appData.popupExitMessage) {
+    (void) MessageBox(hwndMain, str, label, MB_OK|
+                     (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));
+  }
+  ExitEvent(exitStatus);
+}
+
+
+VOID
+DisplayInformation(char *str)
+{
+  (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);
+}
+
+
+typedef struct {
+  char *title, *question, *replyPrefix;
+  ProcRef pr;
+} QuestionParams;
+
+LRESULT CALLBACK
+QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  static QuestionParams *qp;
+  char reply[MSG_SIZ];
+  int len, err;
+
+  switch (message) {
+  case WM_INITDIALOG:
+    qp = (QuestionParams *) lParam;
+    CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));
+    SetWindowText(hDlg, qp->title);
+    SetDlgItemText(hDlg, OPT_QuestionText, qp->question);
+    SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));
+    return FALSE;
+
+  case WM_COMMAND:
+    switch (LOWORD(wParam)) {
+    case IDOK:
+      strcpy(reply, qp->replyPrefix);
+      if (*reply) strcat(reply, " ");
+      len = strlen(reply);
+      GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);
+      strcat(reply, "\n");
+      OutputToProcess(qp->pr, reply, strlen(reply), &err);
+      EndDialog(hDlg, TRUE);
+      if (err) DisplayFatalError("Error writing to chess program", err, 1);
+      return TRUE;
+    case IDCANCEL:
+      EndDialog(hDlg, FALSE);
+      return TRUE;
+    default:
+      break;
+    }
+    break;
+  }
+  return FALSE;
+}
+
+VOID
+AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)
+{
+    QuestionParams qp;
+    FARPROC lpProc;
+    
+    qp.title = title;
+    qp.question = question;
+    qp.replyPrefix = replyPrefix;
+    qp.pr = pr;
+    lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);
+    DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),
+      hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);
+    FreeProcInstance(lpProc);
+}
+
+
+VOID
+DisplayIcsInteractionTitle(char *str)
+{
+  char consoleTitle[MSG_SIZ];
+
+  sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);
+  SetWindowText(hwndConsole, consoleTitle);
+}
+
+void
+DrawPosition(int fullRedraw, Board board)
+{
+  HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); 
+}
+
+
+VOID
+ResetFrontEnd()
+{
+  fromX = fromY = -1;
+  if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {
+    dragInfo.pos.x = dragInfo.pos.y = -1;
+    dragInfo.pos.x = dragInfo.pos.y = -1;
+    dragInfo.lastpos = dragInfo.pos;
+    dragInfo.start.x = dragInfo.start.y = -1;
+    dragInfo.from = dragInfo.start;
+    ReleaseCapture();
+    DrawPosition(TRUE, NULL);
+  }
+}
+
+
+VOID
+CommentPopUp(char *title, char *str)
+{
+  HWND hwnd = GetActiveWindow();
+  EitherCommentPopUp(0, title, str, FALSE);
+  SetActiveWindow(hwnd);
+}
+
+VOID
+CommentPopDown(void)
+{
+  CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);
+  if (commentDialog) {
+    ShowWindow(commentDialog, SW_HIDE);
+  }
+  commentDialogUp = FALSE;
+}
+
+VOID
+EditCommentPopUp(int index, char *title, char *str)
+{
+  EitherCommentPopUp(index, title, str, TRUE);
+}
+
+
+VOID
+RingBell()
+{
+  MyPlaySound(&sounds[(int)SoundMove]);
+}
+
+VOID PlayIcsWinSound()
+{
+  MyPlaySound(&sounds[(int)SoundIcsWin]);
+}
+
+VOID PlayIcsLossSound()
+{
+  MyPlaySound(&sounds[(int)SoundIcsLoss]);
+}
+
+VOID PlayIcsDrawSound()
+{
+  MyPlaySound(&sounds[(int)SoundIcsDraw]);
+}
+
+VOID PlayIcsUnfinishedSound()
+{
+  MyPlaySound(&sounds[(int)SoundIcsUnfinished]);
+}
+
+VOID
+PlayAlarmSound()
+{
+  MyPlaySound(&sounds[(int)SoundAlarm]);
+}
+
+
+VOID
+EchoOn()
+{
+  HWND hInput;
+  consoleEcho = TRUE;
+  hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+  SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);
+  SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);
+}
+
+
+VOID
+EchoOff()
+{
+  CHARFORMAT cf;
+  HWND hInput;
+  consoleEcho = FALSE;
+  hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);
+  /* This works OK: set text and background both to the same color */
+  cf = consoleCF;
+  cf.crTextColor = COLOR_ECHOOFF;
+  SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
+  SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);
+}
+
+/* No Raw()...? */
+
+void Colorize(ColorClass cc, int continuation)
+{
+  currentColorClass = cc;
+  consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
+  consoleCF.crTextColor = textAttribs[cc].color;
+  consoleCF.dwEffects = textAttribs[cc].effects|CFE_LINK;
+  if (!continuation) MyPlaySound(&textAttribs[cc].sound);
+}
+
+char *
+UserName()
+{
+  static char buf[MSG_SIZ];
+  DWORD bufsiz = MSG_SIZ;
+
+  if (!GetUserName(buf, &bufsiz)) {
+    /*DisplayError("Error getting user name", GetLastError());*/
+    strcpy(buf, "User");
+  }
+  return buf;
+}
+
+char *
+HostName()
+{
+  static char buf[MSG_SIZ];
+  DWORD bufsiz = MSG_SIZ;
+
+  if (!GetComputerName(buf, &bufsiz)) {
+    /*DisplayError("Error getting host name", GetLastError());*/
+    strcpy(buf, "Unknown");
+  }
+  return buf;
+}
+
+
+int
+ClockTimerRunning()
+{
+  return clockTimerEvent != 0;
+}
+
+int
+StopClockTimer()
+{
+  if (clockTimerEvent == 0) return FALSE;
+  KillTimer(hwndMain, clockTimerEvent);
+  clockTimerEvent = 0;
+  return TRUE;
+}
+
+void
+StartClockTimer(long millisec)
+{
+  clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,
+                            (UINT) millisec, NULL);
+}
+
+void
+DisplayWhiteClock(long timeRemaining, int highlight)
+{
+  HDC hdc;
+  hdc = GetDC(hwndMain);
+  if (!IsIconic(hwndMain)) {
+    DisplayAClock(hdc, timeRemaining, highlight, &whiteRect, "White");
+  }
+  if (highlight && iconCurrent == iconBlack) {
+    iconCurrent = iconWhite;
+    PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
+    if (IsIconic(hwndMain)) {
+      DrawIcon(hdc, 2, 2, iconCurrent);
+    }
+  }
+  (void) ReleaseDC(hwndMain, hdc);
+  if (hwndConsole)
+    PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
+}
+
+void
+DisplayBlackClock(long timeRemaining, int highlight)
+{
+  HDC hdc;
+  hdc = GetDC(hwndMain);
+  if (!IsIconic(hwndMain)) {
+    DisplayAClock(hdc, timeRemaining, highlight, &blackRect, "Black");
+  }
+  if (highlight && iconCurrent == iconWhite) {
+    iconCurrent = iconBlack;
+    PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
+    if (IsIconic(hwndMain)) {
+      DrawIcon(hdc, 2, 2, iconCurrent);
+    }
+  }
+  (void) ReleaseDC(hwndMain, hdc);
+  if (hwndConsole)
+    PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);
+}
+
+
+int
+LoadGameTimerRunning()
+{
+  return loadGameTimerEvent != 0;
+}
+
+int
+StopLoadGameTimer()
+{
+  if (loadGameTimerEvent == 0) return FALSE;
+  KillTimer(hwndMain, loadGameTimerEvent);
+  loadGameTimerEvent = 0;
+  return TRUE;
+}
+
+void
+StartLoadGameTimer(long millisec)
+{
+  loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,
+                               (UINT) millisec, NULL);
+}
+
+void
+AutoSaveGame()
+{
+  char *defName;
+  FILE *f;
+  char fileTitle[MSG_SIZ];
+
+  defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");
+  f = OpenFileDialog(hwndMain, TRUE, defName,
+                    appData.oldSaveStyle ? "gam" : "pgn",
+                    GAME_FILT, 
+                    "Save Game to File", NULL, fileTitle, NULL);
+  if (f != NULL) {
+    SaveGame(f, 0, "");
+    fclose(f);
+  }
+}
+
+
+void
+ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)
+{
+  if (delayedTimerEvent != 0) {
+    if (appData.debugMode) {
+      fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");
+    }
+    KillTimer(hwndMain, delayedTimerEvent);
+    delayedTimerEvent = 0;
+    delayedTimerCallback();
+  }
+  delayedTimerCallback = cb;
+  delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,
+                               (UINT) millisec, NULL);
+}
+
+DelayedEventCallback
+GetDelayedEvent()
+{
+  if (delayedTimerEvent) {
+    return delayedTimerCallback;
+  } else {
+    return NULL;
+  }
+}
+
+void
+CancelDelayedEvent()
+{
+  if (delayedTimerEvent) {
+    KillTimer(hwndMain, delayedTimerEvent);
+    delayedTimerEvent = 0;
+  }
+}
+
+/* Start a child process running the given program.
+   The process's standard output can be read from "from", and its
+   standard input can be written to "to".
+   Exit with fatal error if anything goes wrong.
+   Returns an opaque pointer that can be used to destroy the process
+   later.
+*/
+int
+StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)
+{
+#define BUFSIZE 4096
+
+  HANDLE hChildStdinRd, hChildStdinWr,
+    hChildStdoutRd, hChildStdoutWr;
+  HANDLE hChildStdinWrDup, hChildStdoutRdDup;
+  SECURITY_ATTRIBUTES saAttr;
+  BOOL fSuccess;
+  PROCESS_INFORMATION piProcInfo;
+  STARTUPINFO siStartInfo;
+  ChildProc *cp;
+  char buf[MSG_SIZ];
+  DWORD err;
+
+  if (appData.debugMode) {
+    fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);
+  }
+
+  *pr = NoProc;
+
+  /* Set the bInheritHandle flag so pipe handles are inherited. */
+  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+  saAttr.bInheritHandle = TRUE;
+  saAttr.lpSecurityDescriptor = NULL;
+
+  /*
+   * The steps for redirecting child's STDOUT:
+   *     1. Create anonymous pipe to be STDOUT for child.
+   *     2. Create a noninheritable duplicate of read handle,
+   *         and close the inheritable read handle.
+   */
+
+  /* Create a pipe for the child's STDOUT. */
+  if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
+    return GetLastError();
+  }
+
+  /* Duplicate the read handle to the pipe, so it is not inherited. */
+  fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+                            GetCurrentProcess(), &hChildStdoutRdDup, 0,
+                            FALSE,     /* not inherited */
+                            DUPLICATE_SAME_ACCESS);
+  if (! fSuccess) {
+    return GetLastError();
+  }
+  CloseHandle(hChildStdoutRd);
+
+  /*
+   * The steps for redirecting child's STDIN:
+   *     1. Create anonymous pipe to be STDIN for child.
+   *     2. Create a noninheritable duplicate of write handle,
+   *         and close the inheritable write handle.
+   */
+
+  /* Create a pipe for the child's STDIN. */
+  if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
+    return GetLastError();
+  }
+
+  /* Duplicate the write handle to the pipe, so it is not inherited. */
+  fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+                            GetCurrentProcess(), &hChildStdinWrDup, 0,
+                            FALSE,     /* not inherited */
+                            DUPLICATE_SAME_ACCESS);
+  if (! fSuccess) {
+    return GetLastError();
+  }
+  CloseHandle(hChildStdinWr);
+
+  /* Arrange to (1) look in dir for the child .exe file, and
+   * (2) have dir be the child's working directory.  Interpret
+   * dir relative to the directory WinBoard loaded from. */
+  GetCurrentDirectory(MSG_SIZ, buf);
+  SetCurrentDirectory(installDir);
+  SetCurrentDirectory(dir);
+
+  /* Now create the child process. */
+
+  siStartInfo.cb = sizeof(STARTUPINFO);
+  siStartInfo.lpReserved = NULL;
+  siStartInfo.lpDesktop = NULL;
+  siStartInfo.lpTitle = NULL;
+  siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+  siStartInfo.cbReserved2 = 0;
+  siStartInfo.lpReserved2 = NULL;
+  siStartInfo.hStdInput = hChildStdinRd;
+  siStartInfo.hStdOutput = hChildStdoutWr;
+  siStartInfo.hStdError = hChildStdoutWr;
+
+  fSuccess = CreateProcess(NULL,
+                          cmdLine,        /* command line */
+                          NULL,           /* process security attributes */
+                          NULL,           /* primary thread security attrs */
+                          TRUE,           /* handles are inherited */
+                          DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
+                          NULL,           /* use parent's environment */
+                          NULL,
+                          &siStartInfo, /* STARTUPINFO pointer */
+                          &piProcInfo); /* receives PROCESS_INFORMATION */
+
+  err = GetLastError();
+  SetCurrentDirectory(buf); /* return to prev directory */
+  if (! fSuccess) {
+    return err;
+  }
+
+  /* Close the handles we don't need in the parent */
+  CloseHandle(piProcInfo.hThread);
+  CloseHandle(hChildStdinRd);
+  CloseHandle(hChildStdoutWr);
+
+  /* Prepare return value */
+  cp = (ChildProc *) calloc(1, sizeof(ChildProc));
+  cp->kind = CPReal;
+  cp->hProcess = piProcInfo.hProcess;
+  cp->pid = piProcInfo.dwProcessId;
+  cp->hFrom = hChildStdoutRdDup;
+  cp->hTo = hChildStdinWrDup;
+
+  *pr = (void *) cp;
+
+  /* Klaus Friedel says that this Sleep solves a problem under Windows
+     2000 where engines sometimes don't see the initial command(s)
+     from WinBoard and hang.  I don't understand how that can happen,
+     but the Sleep is harmless, so I've put it in.  Others have also
+     reported what may be the same problem, so hopefully this will fix
+     it for them too.  */
+  Sleep(500);
+
+  return NO_ERROR;
+}
+
+
+void
+DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)
+{
+  ChildProc *cp;
+
+  cp = (ChildProc *) pr;
+  if (cp == NULL) return;
+
+  switch (cp->kind) {
+  case CPReal:
+    /* TerminateProcess is considered harmful, so... */
+    CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */
+    if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */
+    /* The following doesn't work because the chess program
+       doesn't "have the same console" as WinBoard.  Maybe
+       we could arrange for this even though neither WinBoard
+       nor the chess program uses a console for stdio? */
+    /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/
+    CloseHandle(cp->hProcess);
+    break;
+
+  case CPComm:
+    if (cp->hFrom) CloseHandle(cp->hFrom);
+    break;
+
+  case CPSock:
+    closesocket(cp->sock);
+    WSACleanup();
+    break;
+
+  case CPRcmd:
+    if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */
+    closesocket(cp->sock);
+    closesocket(cp->sock2);
+    WSACleanup();
+    break;
+  }
+  free(cp);
+}
+
+void
+InterruptChildProcess(ProcRef pr)
+{
+  ChildProc *cp;
+
+  cp = (ChildProc *) pr;
+  if (cp == NULL) return;
+  switch (cp->kind) {
+  case CPReal:
+    /* The following doesn't work because the chess program
+       doesn't "have the same console" as WinBoard.  Maybe
+       we could arrange for this even though neither WinBoard
+       nor the chess program uses a console for stdio */
+    /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/
+    break;
+
+  case CPComm:
+  case CPSock:
+    /* Can't interrupt */
+    break;
+
+  case CPRcmd:
+    send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */
+    break;
+  }
+}
+
+
+int
+OpenTelnet(char *host, char *port, ProcRef *pr)
+{
+  char cmdLine[MSG_SIZ];
+
+  if (port[0] == NULLCHAR) {
+    sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
+  } else {
+    sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
+  }
+  return StartChildProcess(cmdLine, "", pr);
+}
+
+
+/* Code to open TCP sockets */
+
+int
+OpenTCP(char *host, char *port, ProcRef *pr)
+{
+  ChildProc *cp;
+  int err;
+  SOCKET s;
+  struct sockaddr_in sa, mysa;
+  struct hostent FAR *hp;
+  unsigned short uport;
+  WORD wVersionRequested;
+  WSADATA wsaData;
+
+  /* Initialize socket DLL */
+  wVersionRequested = MAKEWORD(1, 1);
+  err = WSAStartup(wVersionRequested, &wsaData);
+  if (err != 0) return err;
+
+  /* Make socket */
+  if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+    err = WSAGetLastError();
+    WSACleanup();
+    return err;
+  }
+
+  /* Bind local address using (mostly) don't-care values.
+   */
+  memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
+  mysa.sin_family = AF_INET;
+  mysa.sin_addr.s_addr = INADDR_ANY;
+  uport = (unsigned short) 0;
+  mysa.sin_port = htons(uport);
+  if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
+      == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    WSACleanup();
+    return err;
+  }
+
+  /* Resolve remote host name */
+  memset((char *) &sa, 0, sizeof(struct sockaddr_in));
+  if (!(hp = gethostbyname(host))) {
+    unsigned int b0, b1, b2, b3;
+
+    err = WSAGetLastError();
+
+    if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
+      hp = (struct hostent *) calloc(1, sizeof(struct hostent));
+      hp->h_addrtype = AF_INET;
+      hp->h_length = 4;
+      hp->h_addr_list = (char **) calloc(2, sizeof(char *));
+      hp->h_addr_list[0] = (char *) malloc(4);
+      hp->h_addr_list[0][0] = (char) b0;
+      hp->h_addr_list[0][1] = (char) b1;
+      hp->h_addr_list[0][2] = (char) b2;
+      hp->h_addr_list[0][3] = (char) b3;
+    } else {
+      WSACleanup();
+      return err;
+    }
+  }
+  sa.sin_family = hp->h_addrtype;
+  uport = (unsigned short) atoi(port);
+  sa.sin_port = htons(uport);
+  memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
+
+  /* Make connection */
+  if (connect(s, (struct sockaddr *) &sa,
+             sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    WSACleanup();
+    return err;
+  }
+
+  /* Prepare return value */
+  cp = (ChildProc *) calloc(1, sizeof(ChildProc));
+  cp->kind = CPSock;
+  cp->sock = s;
+  *pr = (ProcRef *) cp;
+
+  return NO_ERROR;
+}
+
+int
+OpenCommPort(char *name, ProcRef *pr)
+{
+  HANDLE h;
+  COMMTIMEOUTS ct;
+  ChildProc *cp;
+  char fullname[MSG_SIZ];
+
+  if (*name != '\\')
+    sprintf(fullname, "\\\\.\\%s", name);
+  else
+    strcpy(fullname, name);
+
+  h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
+                0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+  if (h == (HANDLE) -1) {
+    return GetLastError();
+  }
+  hCommPort = h;
+
+  if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();
+
+  /* Accumulate characters until a 100ms pause, then parse */
+  ct.ReadIntervalTimeout = 100;
+  ct.ReadTotalTimeoutMultiplier = 0;
+  ct.ReadTotalTimeoutConstant = 0;
+  ct.WriteTotalTimeoutMultiplier = 0;
+  ct.WriteTotalTimeoutConstant = 0;
+  if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();
+
+  /* Prepare return value */
+  cp = (ChildProc *) calloc(1, sizeof(ChildProc));
+  cp->kind = CPComm;
+  cp->hFrom = h;
+  cp->hTo = h;
+  *pr = (ProcRef *) cp;
+
+  return NO_ERROR;
+}
+
+int
+OpenLoopback(ProcRef *pr)
+{
+  DisplayFatalError("Not implemented", 0, 1);
+  return NO_ERROR;
+}
+
+
+int
+OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
+{
+  ChildProc *cp;
+  int err;
+  SOCKET s, s2, s3;
+  struct sockaddr_in sa, mysa;
+  struct hostent FAR *hp;
+  unsigned short uport;
+  WORD wVersionRequested;
+  WSADATA wsaData;
+  int fromPort;
+  char stderrPortStr[MSG_SIZ];
+
+  /* Initialize socket DLL */
+  wVersionRequested = MAKEWORD(1, 1);
+  err = WSAStartup(wVersionRequested, &wsaData);
+  if (err != 0) return err;
+
+  /* Resolve remote host name */
+  memset((char *) &sa, 0, sizeof(struct sockaddr_in));
+  if (!(hp = gethostbyname(host))) {
+    unsigned int b0, b1, b2, b3;
+
+    err = WSAGetLastError();
+
+    if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {
+      hp = (struct hostent *) calloc(1, sizeof(struct hostent));
+      hp->h_addrtype = AF_INET;
+      hp->h_length = 4;
+      hp->h_addr_list = (char **) calloc(2, sizeof(char *));
+      hp->h_addr_list[0] = (char *) malloc(4);
+      hp->h_addr_list[0][0] = (char) b0;
+      hp->h_addr_list[0][1] = (char) b1;
+      hp->h_addr_list[0][2] = (char) b2;
+      hp->h_addr_list[0][3] = (char) b3;
+    } else {
+      WSACleanup();
+      return err;
+    }
+  }
+  sa.sin_family = hp->h_addrtype;
+  uport = (unsigned short) 514;
+  sa.sin_port = htons(uport);
+  memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
+
+  /* Bind local socket to unused "privileged" port address
+   */
+  s = INVALID_SOCKET;
+  memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
+  mysa.sin_family = AF_INET;
+  mysa.sin_addr.s_addr = INADDR_ANY;
+  for (fromPort = 1023;; fromPort--) {
+    if (fromPort < 0) {
+      WSACleanup();
+      return WSAEADDRINUSE;
+    }
+    if (s == INVALID_SOCKET) {
+      if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+       err = WSAGetLastError();
+       WSACleanup();
+       return err;
+      }
+    }
+    uport = (unsigned short) fromPort;
+    mysa.sin_port = htons(uport);
+    if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
+       == SOCKET_ERROR) {
+      err = WSAGetLastError();
+      if (err == WSAEADDRINUSE) continue;
+      WSACleanup();
+      return err;
+    }
+    if (connect(s, (struct sockaddr *) &sa,
+      sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
+      err = WSAGetLastError();
+      if (err == WSAEADDRINUSE) {
+       closesocket(s);
+        s = -1;
+       continue;
+      }
+      WSACleanup();
+      return err;
+    }
+    break;
+  }
+
+  /* Bind stderr local socket to unused "privileged" port address
+   */
+  s2 = INVALID_SOCKET;
+  memset((char *) &mysa, 0, sizeof(struct sockaddr_in));
+  mysa.sin_family = AF_INET;
+  mysa.sin_addr.s_addr = INADDR_ANY;
+  for (fromPort = 1023;; fromPort--) {
+    if (fromPort < 0) {
+      (void) closesocket(s);
+      WSACleanup();
+      return WSAEADDRINUSE;
+    }
+    if (s2 == INVALID_SOCKET) {
+      if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+       err = WSAGetLastError();
+       closesocket(s);
+       WSACleanup();
+       return err;
+      }
+    }
+    uport = (unsigned short) fromPort;
+    mysa.sin_port = htons(uport);
+    if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))
+       == SOCKET_ERROR) {
+      err = WSAGetLastError();
+      if (err == WSAEADDRINUSE) continue;
+      (void) closesocket(s);
+      WSACleanup();
+      return err;
+    }
+    if (listen(s2, 1) == SOCKET_ERROR) {
+      err = WSAGetLastError();
+      if (err == WSAEADDRINUSE) {
+       closesocket(s2);
+       s2 = INVALID_SOCKET;
+       continue;
+      }
+      (void) closesocket(s);
+      (void) closesocket(s2);
+      WSACleanup();
+      return err;
+    }
+    break;
+  }
+  sprintf(stderrPortStr, "%d", fromPort);
+
+  if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    (void) closesocket(s);
+    (void) closesocket(s2);
+    WSACleanup();
+    return err;
+  }
+
+  if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    (void) closesocket(s);
+    (void) closesocket(s2);
+    WSACleanup();
+    return err;
+  }
+  if (*user == NULLCHAR) user = UserName();
+  if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    (void) closesocket(s);
+    (void) closesocket(s2);
+    WSACleanup();
+    return err;
+  }
+  if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    (void) closesocket(s);
+    (void) closesocket(s2);
+    WSACleanup();
+    return err;
+  }
+
+  if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {
+    err = WSAGetLastError();
+    (void) closesocket(s);
+    (void) closesocket(s2);
+    WSACleanup();
+    return err;
+  }
+  (void) closesocket(s2);  /* Stop listening */
+
+  /* Prepare return value */
+  cp = (ChildProc *) calloc(1, sizeof(ChildProc));
+  cp->kind = CPRcmd;
+  cp->sock = s;
+  cp->sock2 = s3;
+  *pr = (ProcRef *) cp;
+
+  return NO_ERROR;
+}
+
+
+InputSourceRef
+AddInputSource(ProcRef pr, int lineByLine,
+              InputCallback func, VOIDSTAR closure)
+{
+  InputSource *is, *is2;
+  ChildProc *cp = (ChildProc *) pr;
+
+  is = (InputSource *) calloc(1, sizeof(InputSource));
+  is->lineByLine = lineByLine;
+  is->func = func;
+  is->closure = closure;
+  is->second = NULL;
+  is->next = is->buf;
+  if (pr == NoProc) {
+    is->kind = CPReal;
+    consoleInputSource = is;
+  } else {
+    is->kind = cp->kind;
+    switch (cp->kind) {
+    case CPReal:
+      is->hFile = cp->hFrom;
+      cp->hFrom = NULL; /* now owned by InputThread */
+      is->hThread =
+       CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,
+                    (LPVOID) is, 0, &is->id);
+      break;
+
+    case CPComm:
+      is->hFile = cp->hFrom;
+      cp->hFrom = NULL; /* now owned by InputThread */
+      is->hThread =
+       CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,
+                    (LPVOID) is, 0, &is->id);
+      break;
+
+    case CPSock:
+      is->sock = cp->sock;
+      is->hThread =
+       CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
+                    (LPVOID) is, 0, &is->id);
+      break;
+
+    case CPRcmd:
+      is2 = (InputSource *) calloc(1, sizeof(InputSource));
+      *is2 = *is;
+      is->sock = cp->sock;
+      is->second = is2;
+      is2->sock = cp->sock2;
+      is2->second = is2;
+      is->hThread =
+       CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
+                    (LPVOID) is, 0, &is->id);
+      is2->hThread =
+       CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,
+                    (LPVOID) is2, 0, &is2->id);
+      break;
+    }
+  }
+  return (InputSourceRef) is;
+}
+
+void
+RemoveInputSource(InputSourceRef isr)
+{
+  InputSource *is;
+
+  is = (InputSource *) isr;
+  is->hThread = NULL;  /* tell thread to stop */
+  CloseHandle(is->hThread);
+  if (is->second != NULL) {
+    is->second->hThread = NULL;
+    CloseHandle(is->second->hThread);
+  }
+}
+
+
+int
+OutputToProcess(ProcRef pr, char *message, int count, int *outError)
+{
+  DWORD dOutCount;
+  int outCount = SOCKET_ERROR;
+  ChildProc *cp = (ChildProc *) pr;
+  static OVERLAPPED ovl;
+
+  if (pr == NoProc) {
+    ConsoleOutput(message, count, FALSE);
+    return count;
+  } 
+
+  if (ovl.hEvent == NULL) {
+    ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+  }
+  ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;
+
+  switch (cp->kind) {
+  case CPSock:
+  case CPRcmd:
+    outCount = send(cp->sock, message, count, 0);
+    if (outCount == SOCKET_ERROR) {
+      *outError = WSAGetLastError();
+    } else {
+      *outError = NO_ERROR;
+    }
+    break;
+
+  case CPReal:
+    if (WriteFile(((ChildProc *)pr)->hTo, message, count,
+                 &dOutCount, NULL)) {
+      *outError = NO_ERROR;
+      outCount = (int) dOutCount;
+    } else {
+      *outError = GetLastError();
+    }
+    break;
+
+  case CPComm:
+    *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,
+                           &dOutCount, &ovl);
+    if (*outError == NO_ERROR) {
+      outCount = (int) dOutCount;
+    }
+    break;
+  }
+  return outCount;
+}
+
+int
+OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,
+                      long msdelay)
+{
+  /* Ignore delay, not implemented for WinBoard */
+  return OutputToProcess(pr, message, count, outError);
+}
+
+
+void
+CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,
+                       char *buf, int count, int error)
+{
+  DisplayFatalError("Not implemented", 0, 1);
+}
+
+/* see wgamelist.c for Game List functions */
+/* see wedittags.c for Edit Tags functions */
+
+
+VOID
+ICSInitScript()
+{
+  FILE *f;
+  char buf[MSG_SIZ];
+  char *dummy;
+
+  if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {
+    f = fopen(buf, "r");
+    if (f != NULL) {
+      ProcessICSInitScript(f);
+      fclose(f);
+    }
+  }
+}
+
+
+VOID
+StartAnalysisClock()
+{
+  if (analysisTimerEvent) return;
+  analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,
+                                       (UINT) 1000, NULL);
+}
+
+LRESULT CALLBACK 
+NameSubClass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+/*
+  HWND Name;
+  CHARRANGE pos;
+*/
+       switch (message) {
+               switch (wParam) {
+                       case VK_PRIOR:
+                               return 0;
+                       case VK_NEXT:
+                       return 0;
+               }
+               break;
+       }
+       return (*NameWindowProc)(hwnd, message, wParam, lParam);        
+}
+
+
+LRESULT CALLBACK 
+PvSubClass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  HWND pvDisplay;
+  int GetSel;
+  char buf[MSG_SIZ];
+  pvDisplay    = GetDlgItem(analysisDialog, OPT_AnalysisText); 
+  
+  switch (message) {
+               case WM_MBUTTONUP:
+               case WM_RBUTTONUP: 
+                       {
+                       POINT pt;
+                       pt.x = LOWORD(lParam);
+                       pt.y = HIWORD(lParam);
+                       if (appData.icsActive) {
+                               MenuPopup(pvDisplay, pt, LoadMenu(hInst, "ICSENGINEROOM"), -1);
+                       } else {
+                               MenuPopup(pvDisplay, pt, LoadMenu(hInst, "ENGINEROOM"), -1);
+                       }
+                       }
+               break;
+               case WM_COMMAND:
+                       switch (LOWORD(wParam)) {
+                               case IDM_SelectAll:
+                                       GetSel = SendMessage(hwnd, LB_GETCOUNT, 0, 0);                                                                  
+                                       SendMessage(hwnd, LB_SETSEL, GetSel, -1);
+                               case IDM_Copy:
+                                       GetSel = SendMessage(hwnd, LB_GETCOUNT, 0, 0);
+                                       SendMessage(hwnd, LB_SETSEL, GetSel, -1);
+                                   SendMessage(hwnd, LB_GETTEXT, 0, (LPARAM)&buf);
+
+
+                                       SendMessage(hwnd, WM_COPY, 0, 0);
+                               case IDM_SelPaste:
+                                       GetSel = SendMessage(hwnd, LB_GETCOUNT, 0, 0);                                                                  
+                                       SendMessage(hwnd, LB_SETSEL, GetSel, -1);
+                                       SendMessage(hwnd, LB_GETTEXT, 0, (LPARAM)&buf);
+
+                                       /*
+                                       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
+                                       SendMessage(hwnd, WM_COPY, 0, 0);
+                                       */
+                               return 0;
+                               case IDM_MachineWhite:
+                                       SendMessage(hwndMain, WM_COMMAND, IDM_MachineWhite, 0);
+                                       break;
+                               case IDM_MachineBlack:
+                                       SendMessage(hwndMain, WM_COMMAND, IDM_MachineBlack, 0);
+                                       break;
+                               case IDM_AnalysisMode:
+                                       SendMessage(hwndMain, WM_COMMAND, IDM_AnalysisMode, 0);
+                                       break;
+                               case IDM_MoveNow:
+                                       GuiCommand(1,0);
+                                       break;
+                               case IDM_EnginesButton:
+                                       if (first.analyzing == TRUE || first.maybeThinking == TRUE) {
+                                               SetDlgItemText(analysisDialog, OPT_engineStart, "Start Engine");
+                                       } else if (first.analyzing == FALSE || first.maybeThinking == FALSE) {
+                                               SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
+                                       }
+                                       GuiCommand(2,0);
+                                       break;
+                               case IDM_OperatorTime:
+                               default:
+                               break;
+                       }
+       }
+       return (*PvWindowProc)(hwnd, message, wParam, lParam);  
+}
+
+
+VOID
+AnalysisDialogEnable(HWND hDlg)
+{
+       
+       #define IS_CHECKED(x) (Boolean)IsDlgButtonChecked(hDlg, (x))
+       appData.engineStatLine    = IS_CHECKED(OPT_engineStatLine);
+       if (!appData.engineTourneyMode) {
+               appData.engineTourneyMode = IS_CHECKED(OPT_engineTourneyMode);
+       }
+       #undef IS_CHECKED
+       
+       #define ENABLE_DLG_ITEM(x,y) EnableWindow(GetDlgItem(hDlg,(x)), (y))
+       if (appData.engineTourneyMode) {
+                       /* Freeze GUI */
+                       EnableWindow(GetDlgItem(hDlg, OPT_engineStart), FALSE);
+                       EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
+                       /* disable myself B-) */
+                       EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), FALSE);
+                       EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), FALSE);
+                       EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), FALSE);
+                       return;
+       } else {
+               if (appData.icsActive) {
+                       EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), FALSE);
+                       if (gameMode == IcsObserving) {
+                               EnableWindow(GetDlgItem(hDlg, OPT_engineStart), FALSE);
+                               EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
+                               EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), FALSE);
+                               EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), FALSE);
+                       } else {
+                               EnableWindow(GetDlgItem(hDlg, OPT_engineStart), FALSE);
+                               EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
+                               EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
+                               EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), TRUE);
+                       }
+               /* normal mode */
+               } else {
+                       /* mh, all should allow ;-) */
+                       switch (gameMode) {
+                               case AnalyzeMode:
+                               case AnalyzeFile:
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), FALSE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), FALSE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE); 
+                               break;
+                               case MachinePlaysWhite:
+                                       if (WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), TRUE);
+                                       if (!WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), TRUE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
+                                       break;
+                               case MachinePlaysBlack:
+                                       if (!WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), TRUE);
+                                       if (WhiteOnMove(currentMove)) EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), TRUE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
+                               break;
+                               default:
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStart), TRUE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineMove), FALSE); 
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineTourneyMode), TRUE);
+                                       EnableWindow(GetDlgItem(hDlg, OPT_engineStatLine), TRUE);
+                       }
+                       /* Send OPT_SendToICS only for ICS */
+                       EnableWindow(GetDlgItem(hDlg, OPT_SendToICS), FALSE);
+                       appData.ButtonSendOutPutToICS = FALSE;
+                       appData.SendOutPutToICS = 0;
+               }
+       }
+       #undef ENABLE_DLG_ITEM
+}
+       
+       
+LRESULT CALLBACK
+AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  static HWND pvDisplay;
+  static HWND Name;
+  static HANDLE depth;
+  static HANDLE nps;
+  static HANDLE nodes;
+  static HANDLE score;
+  static HANDLE move_nr;
+  static HANDLE time;
+  static HANDLE statistic;
+  static HANDLE tourney;
+  static HANDLE OutPutSendToICS;
+
+  RECT rect;
+  RECT rectPV;
+  RECT rectStatistic;
+  RECT rectTourney;
+
+  RECT rc;
+  LPMEASUREITEMSTRUCT lpmis;
+  LPARAM lparam;
+  
+  static int sizeX, sizeY;
+  int newSizeX, newSizeY, flags;
+  static int newTextHeight, newTextWidth;
+
+  MINMAXINFO *mmi;
+
+  COLORREF PvBkColor = RGB(224,218,222);
+  COLORREF NameColor = RGB(171,216,173);
+  COLORREF FailLow   = RGB(255, 255, 34);
+  COLORREF FailHigh  = RGB(255, 0, 0);
+  /* 
+   * COLORREF TourneyColor = RGB(235, 80, 71); 
+   */
+  switch (message) {
+  case WM_INITDIALOG: /* message: initialize dialog box */
+    /* Initialize the dialog items */
+    pvDisplay          =   GetDlgItem(hDlg, OPT_AnalysisText); 
+       depth                   =   GetDlgItem(hDlg, OPT_engineDepth);
+       nps                             =   GetDlgItem(hDlg, OPT_engineNPS);
+       nodes                   =   GetDlgItem(hDlg, OPT_engineNodes);
+       score                   =   GetDlgItem(hDlg, OPT_engineScore);
+       Name                    =       GetDlgItem(hDlg, OPT_engineName);
+       move_nr                 =       GetDlgItem(hDlg, OPT_engineMoveNr);
+       time                    =       GetDlgItem(hDlg, OPT_engineTime);
+       statistic               =       GetDlgItem(hDlg, OPT_engineStatLine);
+       tourney                 =       GetDlgItem(hDlg, OPT_engineTourneyMode);
+       OutPutSendToICS =   GetDlgItem(hDlg, OPT_SendToICS);
+
+       #define CHECK_BOX(x,y) CheckDlgButton(hDlg, (x), (BOOL)(y))
+       #define IS_CHECKED(x) (Boolean)IsDlgButtonChecked(hDlg, (x))
+       AnalysisDialogEnable(hDlg);
+       SetWindowText(hDlg, "Winboard Engine Room"); 
+       NameWindowProc = (WNDPROC)
+       SetWindowLong(Name, GWL_WNDPROC, (LONG) NameSubClass);
+       PvWindowProc = (WNDPROC)
+       SetWindowLong(pvDisplay, GWL_WNDPROC, (LONG) PvSubClass);
+       
+       if (!analysisDialog) {
+               analysisDialog = hDlg;
+               flags = SWP_NOZORDER; 
+               GetClientRect(hDlg, &rect);
+               sizeX = rect.right;
+               sizeY = rect.bottom;
+               SendMessage(pvDisplay, EM_SETBKGNDCOLOR, FALSE, PvBkColor);
+               SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameColor);
+               /* gameMode - we do a check */
+               AnalysisDialogEnable(hDlg);
+               if (analysisH < 200 || analysisW < 300) {
+                       /* center - safty position at startup after broken*/
+                       analysisH = 330;
+                       analysisW = 518;
+                       CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+               }
+               if (analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {
+                               WINDOWPLACEMENT wp;
+                               EnsureOnScreen(&analysisX, &analysisY);
+                   wp.length = sizeof(WINDOWPLACEMENT);
+                   wp.flags = WPF_RESTORETOMAXIMIZED;
+                   wp.showCmd = SW_SHOW|SW_RESTORE;
+                   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;
+                   wp.rcNormalPosition.left = analysisX;
+                   wp.rcNormalPosition.right = analysisX + analysisW;
+                   wp.rcNormalPosition.top = analysisY;
+                   wp.rcNormalPosition.bottom = analysisY + analysisH;
+                   SetWindowPlacement(hDlg, &wp);
+                   GetClientRect(hDlg, &rect);
+                               GetClientRect(pvDisplay, &rectPV);
+                               GetClientRect(statistic, &rectStatistic);
+                               GetClientRect(tourney, &rectTourney);
+                   newSizeX = rect.right;
+                   newSizeY = rect.bottom;
+                sizeX = newSizeX;
+                   sizeY = newSizeY;                   
+           }
+               /* set focus to main window on start */
+               SetFocus(hwndMain);
+               /* Check Send engine output to ICS */
+               CHECK_BOX(OPT_SendToICS, appData.ButtonSendOutPutToICS);
+       }
+    return FALSE;
+
+  case WM_MEASUREITEM:
+        // lpmis = (LPMEASUREITEMSTRUCT) lparam; 
+        // GetWindowRect(GetDlgItem(pvDisplay, lpmis->CtlID), &rc);    
+         break;
+
+  case WM_DRAWITEM:
+       break;
+
+  case WM_COMMAND: /* message: received a command */
+       CHECK_BOX(OPT_engineStatLine, appData.engineStatLine);
+       CHECK_BOX(OPT_engineTourneyMode, appData.engineTourneyMode);
+
+         switch (LOWORD(wParam)) {
+       case OPT_engineMove:
+               GuiCommand(1, 0);
+               break;
+       case OPT_engineStart:
+               if (first.analyzing == TRUE || first.maybeThinking == TRUE) {
+                       SetDlgItemText(analysisDialog, OPT_engineStart, "Start Engine");
+               } else if (first.analyzing == FALSE || first.maybeThinking == FALSE) {
+                       SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
+               }
+               GuiCommand(2, 0);
+               break;
+       case OPT_engineStatLine:
+               /* over checkbox vars */
+               break;
+       case OPT_SendToICS:
+               appData.ButtonSendOutPutToICS = IS_CHECKED(OPT_SendToICS);
+
+               if (appData.ButtonSendOutPutToICS) {
+                       appData.SendOutPutToICS = 1;
+               } else {
+                   appData.SendOutPutToICS = 0; 
+               }
+               break;
+       case OPT_engineTourneyMode:
+               /*appData.engineTourneyMode = TRUE;*/
+                MessageBox(hDlg, "Tourney Mode: Disable a lot of WB function.\rYou can leave this mode by closing Engine Room Window !\r\
+This mode is for events suchs as World Champion Chips\rbut not ready at the moment.",
+                 "ICCA/FIDE Tourney Mode", MB_OK);
+               SetWindowText(analysisDialog, "Winboard Engine Room - Tourney Mode");
+               SetWindowText(hwndMain, "Winboard - Tourney Mode");
+               break;
+    case IDCANCEL:
+       /* TourneyMode off */
+       if (appData.engineTourneyMode) {
+                       CheckDlgButton(hDlg, OPT_engineTourneyMode, BST_UNCHECKED);
+                       appData.engineTourneyMode = FALSE;
+                       AnalysisDialogEnable(hDlg);
+                       SetWindowText(analysisDialog, "Winboard Engine Room");
+                       break;    
+       }
+       if (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
+               gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
+                       appData.AnalysisWindow = FALSE;
+                       AnalysisPopDown();
+       } else if (gameMode == IcsObserving) {
+                       appData.icsAnalyzeWindow = FALSE;
+                       AnalysisPopDown();
+       } else {
+                       AnalysisPopDown();
+                       appData.AnalysisWindow = FALSE;
+                       EditGameEvent();
+       }
+       return TRUE;
+    
+       default:
+         AnalysisDialogEnable(hDlg);
+      break;
+    }
+    break;
+
+  case WM_SIZE:
+    newSizeX = LOWORD(lParam);
+    newSizeY = HIWORD(lParam);
+       if (sizeX != newSizeX || sizeY != newSizeY) {
+         GetWindowRect(pvDisplay, &rectPV);
+         newTextWidth = rectPV.right - 
+                 rectPV.left + newSizeX - sizeX;
+      newTextHeight = rectPV.bottom -
+                 rectPV.top + newSizeY - sizeY;
+      if (newTextHeight < 0) {
+               newSizeY += -newTextHeight;
+        newTextHeight = 0;
+      }
+         SetWindowPos(pvDisplay, NULL, 0, 0,
+               newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);
+
+       }
+       sizeX = newSizeX;
+    sizeY = newSizeY;
+    break;
+
+  case WM_GETMINMAXINFO:
+       mmi = (MINMAXINFO *) lParam;
+    mmi->ptMinTrackSize.x = 518;
+    mmi->ptMinTrackSize.y = 250;
+       break;
+
+  case WM_QUERYOPEN:
+               MoveWindow(pvDisplay, sizeX, sizeY, newTextWidth, 
+                       newTextHeight, TRUE);
+               /* engineRoom no longer a parent Window */
+               /* possible that hwndMain is hidden */
+               /* if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE); */
+       break;
+
+  default:
+       AnalysisDialogEnable(hDlg);
+       break;
+  }
+  return FALSE;
+}
+/* Engine Room */
+
+/* style from EngineRoom */
+void
+SetEngineRoomFonts()
+{
+       CHARFORMAT2 pvdisplayf;
+       CHARFORMAT2 enginenamf;
+       HWND PvDisplay;
+       HWND Name;
+       PvDisplay = GetDlgItem(analysisDialog, OPT_AnalysisText);
+       Name = GetDlgItem(analysisDialog, OPT_engineName);
+
+       /* PV screen */
+       pvdisplayf.cbSize               = sizeof(CHARFORMAT);
+       pvdisplayf.bCharSet             = DEFAULT_CHARSET;
+       pvdisplayf.dwMask               = CFM_COLOR|CFM_BOLD|CFM_SPACING|
+                                                         CFM_ITALIC|CFM_CHARSET|CFM_WEIGHT;
+       pvdisplayf.dwEffects    = CFE_ITALIC;
+       pvdisplayf.wWeight              = 2;
+       pvdisplayf.sSpacing             = 10;
+       pvdisplayf.crTextColor  = RGB(0,0,0);
+   
+       /*
+       SendMessage(PvDisplay, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, 
+                       (LPARAM) &pvdisplayf);
+       */
+
+       /* engine Name */       
+       enginenamf.cbSize               = sizeof(CHARFORMAT);
+       enginenamf.bCharSet     = "Arial Black";
+       enginenamf.dwMask               = CFM_BOLD;
+       enginenamf.crTextColor  = RGB(0,0,0); 
+       enginenamf.dwEffects    = CFE_BOLD;
+
+       SendMessage(Name, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, 
+                       (LPARAM) &enginenamf);
+}
+
+/* dirty dirty code: use buffer. I can use EM_*. If i have more time ...
+ * WriteText is allso better. If War Room comes (2 engine window) i will
+ * change to good code. At the moment we work with dirty buffers
+ */
+int i;
+
+VOID
+AnalysisPopUp(pv, depth, nps, nodes, score, move_nr, time, state)
+char* pv;
+char* depth;
+char* nps;
+char* nodes;
+char* score;
+char* move_nr;
+char* time;
+int    state;
+{
+       FARPROC lpProc;
+       HWND pvDisplay;
+       HWND Name;
+       HWND engineStart;
+
+       COLORREF NamePonder = RGB(255, 132, 132); /* opponent/ponder on move */
+       COLORREF NameColor = RGB(171,216,173); /* we are on move */
+       COLORREF NameAnalyze = RGB(148, 148, 248); /* Analyze anf FileanalyzeMode */
+       COLORREF NameObserve = RGB(241, 248, 116); /* ICS observe */
+
+       
+       static char last_pv[MSG_SIZ];
+       char pv_output[16384];
+       static int last_move;
+       static int counter, last_line; 
+       pvDisplay = GetDlgItem(analysisDialog, OPT_AnalysisText);
+       Name = GetDlgItem(analysisDialog, OPT_engineName);
+       engineStart = GetDlgItem(analysisDialog, OPT_engineStart);
+
+       if (!analysisDialog) {
+               lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);
+                       CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),
+                               0, (DLGPROC)lpProc);
+               FreeProcInstance(lpProc);
+               SetEngineRoomFonts();
+               /* engine button don't support icsMode */
+               if (appData.icsActive) {
+                       SetDlgItemText(analysisDialog, OPT_engineStart, "Disable");
+                       SetDlgItemText(analysisDialog, OPT_engineMove, "Disable");
+               } else {
+                       SetDlgItemText(analysisDialog, OPT_engineStart, "Start Engine");
+                       SetDlgItemText(analysisDialog, OPT_engineMove, "Move!");
+               }       
+               /* if state 5 we have a WB bootup init */
+               if (state == 5) {
+                       DisplayAnalysis(0,0);
+                       SendDlgItemMessage(analysisDialog, OPT_AnalysisText, 
+                    LB_ADDSTRING, (WPARAM) 0, 
+                    (LPARAM) "ready for play");
+                       //SetDlgItemText(analysisDialog, OPT_AnalysisText, "ready for play");
+                       return;
+               }
+       }
+
+       /* check if core part say no support for stat line */
+       /* only if new game or so */
+       if (appData.engineStatLine == TRUE) {
+                       CheckDlgButton(analysisDialog, OPT_engineStatLine, BST_CHECKED);
+                       SetDlgItemText(analysisDialog, OPT_engineMoveNr, "disable");
+       } else if (appData.engineStatLine == FALSE) {
+                       CheckDlgButton(analysisDialog, OPT_engineStatLine, BST_UNCHECKED);
+       }
+       
+       /* new move - begin from 0 */
+    if (last_move != currentMove) {
+          SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_RESETCONTENT, 0, 0);
+          last_move = currentMove;
+          counter = 0;
+       }
+       
+       /* Set engine button to engine state */
+       if (pv[0] != NULLCHAR) {
+               if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
+                       if (first.analyzing == TRUE) {
+                               SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
+
+                       }
+               } else if (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {
+                       if (first.maybeThinking) {
+                               SetDlgItemText(analysisDialog, OPT_engineStart, "Stop Engine");
+                       }
+               }
+       }
+          /* colorize EngineName  - really check every action? */
+       /* If we fast yes, think about build a own function
+           * and set it on GameModeEvent()
+               */
+       switch (state) {
+          case 0:
+               SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameColor);
+               break;
+          case 1:
+               SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NamePonder);
+               break;
+          case 2:
+               SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameAnalyze);
+               break;
+          case 3:
+               SendMessage(Name, EM_SETBKGNDCOLOR, FALSE, NameObserve);
+               break;
+       }
+
+  if (pv[0] != NULLCHAR) {   
+               strcpy(pv_output, time);
+               strcat(pv_output, pv);
+               strcpy(engineRoom[counter].mainline, pv_output);
+
+               counter++;
+               
+               /*
+               for (i = 0; i <= counter; i++) {
+                       if (i == 0) {
+                               strcpy(pv_output, engineRoom[i].mainline);
+                               strcat(pv_output, "\r\n");
+                       } else if (i == counter) {
+                               strcat(pv_output, engineRoom[i].mainline); 
+                       } else {
+                               strcat(pv_output, engineRoom[i].mainline);
+                               strcat(pv_output, "\r\n");
+                       }
+               }
+               //SetDlgItemText(analysisDialog, OPT_AnalysisText, pv_output);
+               */
+
+
+               SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_ADDSTRING, (WPARAM) 0, (LPARAM) pv_output);
+       
+               /* mark last pv */
+               SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_SETSEL, 0, -1);
+               SendDlgItemMessage(analysisDialog, OPT_AnalysisText, LB_SETSEL, counter, counter-1);
+                
+               SendMessage(pvDisplay, WM_VSCROLL, SB_BOTTOM, 0);
+       }
+       
+       SetDlgItemText(analysisDialog, OPT_engineDepth, depth);
+       SetDlgItemText(analysisDialog, OPT_engineNPS, nps);
+       SetDlgItemText(analysisDialog, OPT_engineNodes, nodes);
+       SetDlgItemText(analysisDialog, OPT_engineScore, score);
+       SetDlgItemText(analysisDialog, OPT_engineMoveNr, move_nr);
+       SetDlgItemText(analysisDialog, OPT_engineTime, time);
+       SetDlgItemText(analysisDialog, OPT_engineHash, "reserved");
+       SetDlgItemText(analysisDialog, OPT_egtb, "reserved");
+       if (state == 4) {
+               SetDlgItemText(analysisDialog, OPT_engineName, "no support");
+               if (appData.AnalysisWindow) {
+                       /* We need an other window and a *queue* for each engine to 
+                        * read data !!
+                        */
+                               appData.AnalysisWindow = FALSE;
+                               SetDlgItemText(analysisDialog, OPT_AnalysisText,
+                               "Sorry, no Support. Please wait for a coming Engine War Room !");
+                               /* take TourneyMode to block user action ;-) */
+                               if (!appData.engineTourneyMode) appData.engineTourneyMode = TRUE;
+               }
+       } else {
+               /* We don't need this because we have only 1 engine 
+                * and set on start should be enought, but for tests
+                * it's enable at the moment
+                */
+               SetDlgItemText(analysisDialog, OPT_engineName, first.tidy);
+       }
+       
+       strcpy(last_pv, pv);
+       if (analysisDialogUp != TRUE) {
+               analysisDialogUp = TRUE; 
+               ShowWindow(analysisDialog, SW_SHOW);
+       }
+}
+
+VOID
+AnalysisPopDown()
+{
+  if (analysisDialog) {
+    ShowWindow(analysisDialog, SW_HIDE);
+  }
+  analysisDialogUp = FALSE;  
+}
+
+
+VOID
+SetHighlights(int fromX, int fromY, int toX, int toY)
+{
+  highlightInfo.sq[0].x = fromX;
+  highlightInfo.sq[0].y = fromY;
+  highlightInfo.sq[1].x = toX;
+  highlightInfo.sq[1].y = toY;
+}
+
+VOID
+ClearHighlights()
+{
+  highlightInfo.sq[0].x = highlightInfo.sq[0].y = 
+    highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;
+}
+
+VOID
+SetPremoveHighlights(int fromX, int fromY, int toX, int toY)
+{
+  premoveHighlightInfo.sq[0].x = fromX;
+  premoveHighlightInfo.sq[0].y = fromY;
+  premoveHighlightInfo.sq[1].x = toX;
+  premoveHighlightInfo.sq[1].y = toY;
+}
+
+VOID
+ClearPremoveHighlights()
+{
+  premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = 
+    premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;
+}
+
+VOID
+ShutDownFrontEnd()
+{
+  if (saveSettingsOnExit) SaveSettings(settingsFileName);
+  DeleteClipboardTempFiles();
+}
+
+void
+BoardToTop()
+{
+    if (IsIconic(hwndMain))
+      ShowWindow(hwndMain, SW_RESTORE);
+
+    SetActiveWindow(hwndMain);
+}
+
+/*
+ * Prototypes for animation support routines
+ */
+static void ScreenSquare(int column, int row, POINT * pt);
+static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,
+     POINT frames[], int * nFrames);
+
+
+#define kFactor 4
+
+void
+AnimateMove(board, fromX, fromY, toX, toY)
+     Board board;
+     int fromX;
+     int fromY;
+     int toX;
+     int toY;
+{
+  ChessSquare piece;
+  POINT start, finish, mid;
+  POINT frames[kFactor * 2 + 1];
+  int nFrames, n;
+
+  if (!appData.animate) return;
+  if (doingSizing) return;
+  if (fromY < 0 || fromX < 0) return;
+  piece = board[fromY][fromX];
+  if (piece >= EmptySquare) return;
+
+  ScreenSquare(fromX, fromY, &start);
+  ScreenSquare(toX, toY, &finish);
+
+  /* All pieces except knights move in straight line */
+  if (piece != WhiteKnight && piece != BlackKnight) {
+    mid.x = start.x + (finish.x - start.x) / 2;
+    mid.y = start.y + (finish.y - start.y) / 2;
+  } else {
+    /* Knight: make diagonal movement then straight */
+    if (abs(toY - fromY) < abs(toX - fromX)) {
+       mid.x = start.x + (finish.x - start.x) / 2;
+       mid.y = finish.y;
+     } else {
+       mid.x = finish.x;
+       mid.y = start.y + (finish.y - start.y) / 2;
+     }
+  }
+  
+  /* Don't use as many frames for very short moves */
+  if (abs(toY - fromY) + abs(toX - fromX) <= 2)
+    Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
+  else
+    Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
+
+  animInfo.from.x = fromX;
+  animInfo.from.y = fromY;
+  animInfo.to.x = toX;
+  animInfo.to.y = toY;
+  animInfo.lastpos = start;
+  animInfo.piece = piece;
+  for (n = 0; n < nFrames; n++) {
+    animInfo.pos = frames[n];
+    DrawPosition(FALSE, NULL);
+    animInfo.lastpos = animInfo.pos;
+    Sleep(appData.animSpeed);
+  }
+  animInfo.pos = finish;
+  DrawPosition(FALSE, NULL);
+  animInfo.piece = EmptySquare;
+}
+
+/*      Convert board position to corner of screen rect and color       */
+
+static void
+ScreenSquare(column, row, pt)
+     int column; int row; POINT * pt;
+{
+  if (flipView) {
+    pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
+    pt->y = lineGap + row * (squareSize + lineGap);
+  } else {
+    pt->x = lineGap + column * (squareSize + lineGap);
+    pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
+  }
+}
+
+/*      Generate a series of frame coords from start->mid->finish.
+        The movement rate doubles until the half way point is
+        reached, then halves back down to the final destination,
+        which gives a nice slow in/out effect. The algorithmn
+        may seem to generate too many intermediates for short
+        moves, but remember that the purpose is to attract the
+        viewers attention to the piece about to be moved and
+        then to where it ends up. Too few frames would be less
+        noticeable.                                             */
+
+static void
+Tween(start, mid, finish, factor, frames, nFrames)
+     POINT * start; POINT * mid;
+     POINT * finish; int factor;
+     POINT frames[]; int * nFrames;
+{
+  int n, fraction = 1, count = 0;
+
+  /* Slow in, stepping 1/16th, then 1/8th, ... */
+  for (n = 0; n < factor; n++)
+    fraction *= 2;
+  for (n = 0; n < factor; n++) {
+    frames[count].x = start->x + (mid->x - start->x) / fraction;
+    frames[count].y = start->y + (mid->y - start->y) / fraction;
+    count ++;
+    fraction = fraction / 2;
+  }
+  
+  /* Midpoint */
+  frames[count] = *mid;
+  count ++;
+  
+  /* Slow out, stepping 1/2, then 1/4, ... */
+  fraction = 2;
+  for (n = 0; n < factor; n++) {
+    frames[count].x = finish->x - (finish->x - mid->x) / fraction;
+    frames[count].y = finish->y - (finish->y - mid->y) / fraction;
+    count ++;
+    fraction = fraction * 2;
+  }
+  *nFrames = count;
+}
+
+void
+HistorySet(char movelist[][2*MOVE_LEN], int first, int last, int current)
+{
+  /* Currently not implemented in WinBoard */
+}
+
+