/* vi:set sts=4 sw=4:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * os_win32.c
 *
 * Win32 (Windows NT and Windows 95) system-dependent routines.
 * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
 * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
 *
 * George V. Reilly <gvr@halcyon.com> wrote most of this.
 * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
 */

#include <io.h>
#include "vim.h"
#include "globals.h"
#include "option.h"
#include "proto.h"
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <limits.h>
#include <process.h>

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#undef chdir
#include <direct.h>

#if 0
#include <stdarg.h>
#endif


/* Disgusting hack for getting dead keys to work properly on Windows 95.
 * See below for the gory details.  If you're only running on NT or
 * if you're an English-speaking user, you can comment this out. */

/* WARNING: this code is experimental and incomplete and it does not work.
 * Don't use it! */
/* #define WIN95_DEAD_KEYS_HACK */


/* Force all file names to lowercase */
/* #define DOWNCASE_FILENAMES */


/* Record all output and all keyboard & mouse input */
/* #define MCH_WRITE_DUMP */

#ifdef MCH_WRITE_DUMP
FILE* fdDump = NULL;
#endif /* MCH_WRITE_DUMP */


/*
 * When generating prototypes for Win32 on Unix, these lines make the syntax
 * errors disappear.  They do not need to be correct.
 */
#ifdef PROTO
# define HANDLE int
# define SMALL_RECT int
# define COORD int
# define SHORT int
# define WORD int
# define DWORD int
# define BOOL int
# define LPSTR int
# define KEY_EVENT_RECORD int
# define MOUSE_EVENT_RECORD int
# define WINAPI
# define CONSOLE_CURSOR_INFO int
# define LPCSTR char_u *
# define WINBASEAPI
#endif

/* Undocumented API in kernel32.dll needed to work around dead key bug in
 * console-mode applications in NT 4.0.  If you switch keyboard layouts
 * in a console app to a layout that includes dead keys and then hit a
 * dead key, a call to ToAscii will trash the stack.  My thanks to Ian James
 * and Michael Dietrich for helping me figure out this workaround.
 */

/* WINBASEAPI BOOL WINAPI GetConsoleKeyboardLayoutNameA(LPSTR); */
#ifdef __BORLANDC__
typedef BOOL (*PFNGCKLN)(LPSTR);
#else
typedef WINBASEAPI BOOL (*PFNGCKLN)(LPSTR);
#endif
PFNGCKLN    s_pfnGetConsoleKeyboardLayoutName = NULL;

#ifdef __BORLANDC__
// being a more ANSI compliant compiler, BorlandC doesn't define _stricoll:

int _stricoll(char *a, char *b)
{
#if 1
    // this is fast but not correct:
    return stricmp(a,b);
#else
    // the ANSI-ish correct way is to use strxfrm():
    char a_buff[512], b_buff[512];  // file names, so this is enough on Win32
    strxfrm(a_buff, a, 512);
    strxfrm(b_buff, b, 512);
    return strcoll(a_buff, b_buff);
#endif
}
#endif

/* Win32 Console handles for input and output */
static HANDLE g_hConIn	= INVALID_HANDLE_VALUE;
static HANDLE g_hSavOut = INVALID_HANDLE_VALUE;
static HANDLE g_hCurOut = INVALID_HANDLE_VALUE;
static HANDLE g_hConOut = INVALID_HANDLE_VALUE;

/* Win32 Screen buffer,coordinate,console I/O information */
static SMALL_RECT g_srScrollRegion;
static COORD	  g_coord;  /* 0-based, but external coords are 1-based */

/* The attribute of the screen when the editor was started */
static WORD  g_attrDefault = 7;  /* lightgray text on black background */
static WORD  g_attrCurrent;

static int g_fCBrkPressed = FALSE;  /* set by ctrl-break interrupt */
static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */

static DWORD PlatformId(void);
static void termcap_mode_start(void);
static void termcap_mode_end(void);
static void clear_chars(COORD coord, DWORD n);
static void clear_screen(void);
static void clear_to_end_of_display(void);
static void clear_to_end_of_line(void);
static void scroll(unsigned cLines);
static void set_scroll_region(unsigned left, unsigned top,
			      unsigned right, unsigned bottom);
static void insert_lines(unsigned cLines);
static void delete_lines(unsigned cLines);
static void gotoxy(unsigned x, unsigned y);
static void normvideo(void);
static void textattr(WORD wAttr);
static void textcolor(WORD wAttr);
static void textbackground(WORD wAttr);
static void standout(void);
static void standend(void);
static void visual_bell(void);
static void cursor_visible(BOOL fVisible);
static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite, DWORD* pcchWritten);
static char_u tgetch(void);
static BOOL kbhit(void);


/* This symbol is not defined in older versions of the SDK or Visual C++ */

#ifndef VER_PLATFORM_WIN32_WINDOWS
# define VER_PLATFORM_WIN32_WINDOWS 1
#endif

static DWORD g_PlatformId;

/*
 *  Returns VER_PLATFORM_WIN32_NT (NT) or VER_PLATFORM_WIN32_WINDOWS (Win95)
 */
    static DWORD
PlatformId(void)
{
    OSVERSIONINFO ovi;

    ovi.dwOSVersionInfoSize = sizeof(ovi);
    GetVersionEx(&ovi);

    g_PlatformId = ovi.dwPlatformId;

    return g_PlatformId;
}

#define SHIFT  (SHIFT_PRESSED)
#define CTRL   (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
#define ALT    (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED)
#define ALT_GR (RIGHT_ALT_PRESSED  | LEFT_CTRL_PRESSED)


#ifdef WIN95_DEAD_KEYS_HACK

/* The problem that this code attempts to work around is that the
 * console-mode input routines are broken on Windows 95 if you're
 * using dead keys.  A dead key is an accent key, such as acute,
 * grave, or umlaut, that doesn't produce a character by itself,
 * but when followed by another key, produces an accented character,
 * such as a-acute, e-grave, u-umlaut, n-tilde, and so on.  Very
 * useful for most European languages.	English-language keyboard
 * layouts don't use dead keys, as far as I know.
 *
 * Unfortunately, the KEY_EVENT_RECORD contains the wrong data when
 * I get the keystroke that's supposed to have an accented character.
 * The AsciiChar part contains the unaccented character instead.
 *
 * The following code (unsuccessfully) attempts to fake the dead keys.
 * I rely on the fact that the virtual key code and the scan key code
 * are actually correct for both the dead key and the following letter.
 * I set up a second thread that creates a hidden window and then sits
 * in a little message loop.  I send it all the data from the dead key
 * and the following key (each thread has its own keyboard input state),
 * and it sends its own hidden window a suitable sequence of WM_KEYDOWN
 * and WM_KEYUP messages.  TranslateMessage causes these to be turned
 * into WM_DEADCHAR and WM_CHAR messages.  Almost.  It works as long as
 * neither the dead key nor the following key are shifted, but after
 * happens, that it goes to hell in a handbasket.
 *
 * I have spent many, many hours trying all sorts of approaches to get
 * dead keys to work with Windows 95 and I am completely stumped.
 *
 * So why not just have all the input come directly via WM_CHAR?
 * Because this is a console application and I don't know how to do that.
 *
 * So how do you type dead keys?  Answer: you don't, not if you're using
 * Windows 95, not until either Microsoft fixes the bug (and they haven't
 * as of Service Pack 1) or someone (me?) produces a true Windows GUI
 * version of Vim.
 *
 * If someone can come up with some workaround, please, please, please
 * send it to me.
 *
 * You can, however, use digraphs.  They're not as convenient as dead
 * keys, especially if you're used to typing dead keys, but they do
 * work and they're better than nothing.
 *
 * If you're using NT, the dead keys work fine.  At least they do with
 * NT 3.51, Service Pack 3, US Edition.  I haven't tested it with any
 * other version of NT.  I did hear one report that NT3.51, SP3, UK edition
 * had problems with CTRL-vowels, but I don't know about dead keys.
 *
 * /George V. Reilly, 3/15/1996
 *
 * On 24th March, I was told by a senior developer at Microsoft:
 *   Win95 console support has always been and will always be flaky.
 *
 *   The flakiness is unavoidable because we are stuck between the world of
 *   MS-DOS keyboard TSRs like KEYB (which wants to cook the data; important
 *   for international) and the world of Win32.
 *
 *   So keys that don't "exist" in MS-DOS land (like dead keys) have a
 *   very tenuous existence in Win32 console land.  Keys that act
 *   differently between MS-DOS land and Win32 console land (like capslock)
 *   will act flaky.
 *
 *   Don't even *mention* the problems with multiple language keyboard
 *   layouts...
 */

static HWND   g_hwndKbd  = NULL;
static HANDLE g_hthrdKbd = NULL;
static DWORD  g_dwThreadId = 0;

#define WM_SENDDEADKEY (WM_USER+31)


void send_key(UINT uVirtKey, UINT uScanCode, UINT fDown);
void key_down(UINT uVirtKey, UINT uScanCode);
void keystate_down(UINT uCtrlState);
void key_up(UINT uVirtKey, UINT uScanCode);
void keystate_up(UINT uCtrlState);

    static LRESULT CALLBACK
keyboard_wndproc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    static int n, vk, sc;
    char ch, sz[200];

    sprintf(sz, "kbd: msg = %04x, w = %08x, l = %08x\n", uMsg, wParam, lParam);
    OutputDebugString(sz);

    switch (uMsg)
    {
    case WM_SENDDEADKEY:
	{
	    UINT uDeadVirt, uDeadScan, uDeadCtrl, uVirt, uScan, uCtrl;
	    BYTE abKeystate[256];
	    WORD awAnsiCode[2];

	    uDeadVirt = LOBYTE(wParam);
	    uDeadScan = HIBYTE(wParam);
	    uDeadCtrl = HIWORD(wParam);

	    uVirt = LOBYTE(lParam);
	    uScan = HIBYTE(lParam);
	    uCtrl = HIWORD(lParam);

	    GetKeyboardState(abKeystate);

	    memset(abKeystate, 0, sizeof (abKeystate));
	    if (!SetKeyboardState(abKeystate))
	    {
		n = GetLastError();
		sprintf(sz, "setkeyboardstate failed, error %d\n", n);
		OutputDebugString(sz);
	    }

	    ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0),
		    abKeystate, awAnsiCode, 0);

	    if (uDeadCtrl & SHIFT_PRESSED)
		abKeystate[VK_SHIFT] = 0x80;
	    if (uDeadCtrl & CAPSLOCK_ON)
		abKeystate[VK_CAPITAL] = 1;

	    if ((uDeadCtrl & ALT_GR) == ALT_GR)
	    {
		abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
		    abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
	    }

	    if (!SetKeyboardState(abKeystate))
	    {
		n = GetLastError();
		sprintf(sz, "setkeyboardstate failed, error %d\n", n);
		OutputDebugString(sz);
	    }

	    //keystate_down(uDeadCtrl);
	    key_down(uDeadVirt, uDeadScan);
	    key_up(uDeadVirt, uDeadScan);
	    //keystate_up(uDeadCtrl);

	    memset(abKeystate, 0, sizeof (abKeystate));

	    if (uCtrl & SHIFT_PRESSED)
		abKeystate[VK_SHIFT] = 0x80;
	    if (uCtrl & CAPSLOCK_ON)
		abKeystate[VK_CAPITAL] = 1;

	    if ((uCtrl & ALT_GR) == ALT_GR)
	    {
		abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
		    abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
	    }

	    if (!SetKeyboardState(abKeystate))
	    {
		int n = GetLastError();
	    }

	    //keystate_down(uCtrl);
	    key_down(uVirt, uScan);
	    Sleep(30);

	    key_up(uVirt, uScan);
	    //keystate_up(uCtrl);
	}
	break;

    case WM_CHAR:
    case WM_DEADCHAR:
	ch = wParam;
	CharToOemBuff(&ch, &ch, 1);
	sprintf(sz, "WM_%sCHAR: wParam = 0x%2x, Oem = 0x%2x, '%c'\n",
		((uMsg == WM_CHAR) ? "" : "DEAD"),
		wParam, LOBYTE(ch), isprint(ch) ? ch : '?');
	OutputDebugString(sz);

	n = DefWindowProc(hwnd, uMsg, wParam, lParam);
	return n;

    case WM_KEYDOWN:
	vk = wParam;
	sc = HIWORD(lParam);

    default:
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}



    static DWORD WINAPI
keyboard_threadproc(
    LPVOID lpvThreadParam)
{
    const char szKbdTrans[] = "VimKbdXlator";
    WNDCLASS wndclass;
    MSG msg;
    int i = 0;

    wndclass.style = 0;
    wndclass.lpfnWndProc = keyboard_wndproc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = GetModuleHandle(NULL);
    wndclass.hIcon = NULL;
    wndclass.hCursor = NULL;
    wndclass.hbrBackground = NULL;
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szKbdTrans;

    RegisterClass(&wndclass);

    g_hwndKbd = CreateWindow(szKbdTrans, "", 0,
			     CW_USEDEFAULT, CW_USEDEFAULT,
			     CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
			     GetModuleHandle(NULL), NULL);

    while (GetMessage(&msg, NULL, 0, 0))
    {
#if 0
	char sz[200];
	sprintf(sz, "gm: %2d: hwnd = %x, msg = %x, w = %x, l = %x\n",
		++i, msg.hwnd, msg.message, msg.wParam, msg.lParam);
	OutputDebugString(sz);
#endif

	TranslateMessage(&msg);
	DispatchMessage(&msg);
    }

    return msg.wParam;
}



    static BOOL
keyboard_init()
{
    const char sz[] = "aA\xE0\xE1\xE8\xE9\xF1\xD1";
    const char* psz = sz;

    for ( ; *psz; psz++)
    {
	char sz2[100];
	SHORT w = VkKeyScan(*psz);

	sprintf(sz2, "%02x: %04x\n", *psz, w);
	OutputDebugString(sz2);
    }

    g_hthrdKbd = (HANDLE) _beginthreadex(NULL, 0, keyboard_threadproc,
					 (LPVOID) NULL, 0, &g_dwThreadId);

    return (g_hthrdKbd != NULL);
}



    static void
keyboard_exit()
{
    TerminateThread(g_hthrdKbd, 0);
}



    void
send_key(
    UINT uVirtKey,
    UINT uScanCode,
    UINT fDown)
{
    PostMessage(g_hwndKbd,
		fDown ? WM_KEYDOWN : WM_KEYUP,
		uVirtKey,
		MAKELONG(1, uScanCode | (fDown ? 0 : 0x8000)));
    Sleep(20);
}



    void
key_down(
    UINT uVirtKey,
    UINT uScanCode)
{
    send_key(uVirtKey, uScanCode, TRUE);
}



    void
keystate_down(
    UINT uCtrlState)
{
    if (uCtrlState & SHIFT_PRESSED)
	key_down(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0));
    if ((uCtrlState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0)
	key_down(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0));
}



    void
key_up(
    UINT uVirtKey,
    UINT uScanCode)
{
    send_key(uVirtKey, uScanCode, FALSE);
}



    void
keystate_up(
    UINT uCtrlState)
{
    if ((uCtrlState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0)
	key_up(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0));
    if (uCtrlState & SHIFT_PRESSED)
	key_up(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0));
}

#endif /* WIN95_DEAD_KEYS_HACK */



/* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
 * We map function keys to their ANSI terminal equivalents, as produced
 * by ANSI.SYS, for compatibility with the MS-DOS version of Vim.  Any
 * ANSI key with a value >= '\300' is nonstandard, but provided anyway
 * so that the user can have access to all SHIFT-, CTRL-, and ALT-
 * combinations of function/arrow/etc keys.
 */

const static struct {
    WORD    wVirtKey;
    BOOL    fAnsiKey;
    int	    chAlone;
    int	    chShift;
    int	    chCtrl;
    int	    chAlt;
} VirtKeyMap[] = {

/*    Key	ANSI	alone	shift	ctrl	    alt */
    { VK_ESCAPE,FALSE,	ESC,	ESC,	ESC,	    ESC,    },

    { VK_F1,	TRUE,	';',	'T',	'^',	    'h', },
    { VK_F2,	TRUE,	'<',	'U',	'_',	    'i', },
    { VK_F3,	TRUE,	'=',	'V',	'`',	    'j', },
    { VK_F4,	TRUE,	'>',	'W',	'a',	    'k', },
    { VK_F5,	TRUE,	'?',	'X',	'b',	    'l', },
    { VK_F6,	TRUE,	'@',	'Y',	'c',	    'm', },
    { VK_F7,	TRUE,	'A',	'Z',	'd',	    'n', },
    { VK_F8,	TRUE,	'B',	'[',	'e',	    'o', },
    { VK_F9,	TRUE,	'C',	'\\',	'f',	    'p', },
    { VK_F10,	TRUE,	'D',	']',	'g',	    'q', },
    { VK_F11,	TRUE,	'\205',	'\207',	'\211',	    '\213', },
    { VK_F12,	TRUE,	'\206',	'\210',	'\212',	    '\214', },

    { VK_HOME,	TRUE,	'G',	'\302',	'w',	    '\303', },
    { VK_UP,	TRUE,	'H',	'\304',	'\305',	    '\306', },
    { VK_PRIOR,	TRUE,	'I',	'\307',	'\204',	    '\310', }, /*PgUp*/
    { VK_LEFT,	TRUE,	'K',	'\311',	's',	    '\312', },
    { VK_RIGHT,	TRUE,	'M',	'\313',	't',	    '\314', },
    { VK_END,	TRUE,	'O',	'\315',	'u',	    '\316', },
    { VK_DOWN,	TRUE,	'P',	'\317',	'\320',	    '\321', },
    { VK_NEXT,	TRUE,	'Q',	'\322',	'v',	    '\323', }, /*PgDn*/
    { VK_INSERT,TRUE,	'R',	'\324',	'\325',	    '\326', },
    { VK_DELETE,TRUE,	'S',	'\327',	'\330',	    '\331', },

    { VK_SNAPSHOT,TRUE,	0,	0,	0,	    'r', }, /*PrtScrn*/

    /* Most people don't have F13-F20, but what the hell... */
    { VK_F13,	TRUE,	'\332',	'\333',	'\334',	    '\335', },
    { VK_F14,	TRUE,	'\336',	'\337',	'\340',	    '\341', },
    { VK_F15,	TRUE,	'\342',	'\343',	'\344',	    '\345', },
    { VK_F16,	TRUE,	'\346',	'\347',	'\350',	    '\351', },
    { VK_F17,	TRUE,	'\352',	'\353',	'\354',	    '\355', },
    { VK_F18,	TRUE,	'\356',	'\357',	'\360',	    '\361', },
    { VK_F19,	TRUE,	'\362',	'\363',	'\364',	    '\365', },
    { VK_F20,	TRUE,	'\366',	'\367',	'\370',	    '\371', },
};


// The ToAscii bug destroys several registers.	Need to turn off optimization
// or the GetConsoleKeyboardLayoutName hack will fail in non-debug versions

#pragma optimize("", off)


/* The return code indicates key code size. */
    static int
win32_kbd_patch_key(
    KEY_EVENT_RECORD* pker)
{
    static int s_iIsDead = 0;
    static WORD awAnsiCode[2];
    UINT uMods = pker->dwControlKeyState;
    BYTE abKeystate[256];

    if (s_iIsDead == 2)
    {
	pker->uChar.AsciiChar = (CHAR) awAnsiCode[1];
	s_iIsDead = 0;
	return 1;
    }

    if (pker->uChar.AsciiChar != 0)
	return 1;

    memset(abKeystate, 0, sizeof (abKeystate));

    // Should only be non-NULL on NT 4.0
    if (s_pfnGetConsoleKeyboardLayoutName != NULL)
    {
	CHAR szKLID[KL_NAMELENGTH];

	if (s_pfnGetConsoleKeyboardLayoutName(szKLID))
	{
#if 0	    /* hkl is never used!? */
	    HKL hkl = LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
#else
	    (void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
#endif
	}
    }

    /* Clear any pending dead keys */
    ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 0);

    if (uMods & SHIFT_PRESSED)
	abKeystate[VK_SHIFT] = 0x80;
    if (uMods & CAPSLOCK_ON)
	abKeystate[VK_CAPITAL] = 1;

    if ((uMods & ALT_GR) == ALT_GR)
    {
	abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
	    abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
    }

    s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode,
			abKeystate, awAnsiCode, 0);

    if (s_iIsDead > 0)
	pker->uChar.AsciiChar = (CHAR) awAnsiCode[0];

    return s_iIsDead;
}


static BOOL g_fJustGotFocus = FALSE;

/*
 * Decode a KEY_EVENT into one or two keystrokes
 */
    static BOOL
decode_key_event(
    KEY_EVENT_RECORD* pker,
    char_u* pch,
    char_u* pchPending,
    BOOL fDoPost)
{
    int i;
    const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);

#ifdef WIN95_DEAD_KEYS_HACK
    static UINT uDeadVirtKey = 0, uDeadScan = 0, uDeadCtrlState = 0;
    char sz[200];

    sprintf(sz, "Vk = 0x%02x, `%c'; Scan = 0x%02x; "
	    "Char = %3d, 0x%02x, `%c'; Ctrl = 0x%02x, Post = %d\n",
	    pker->wVirtualKeyCode,
	    isprint(pker->wVirtualKeyCode) ? pker->wVirtualKeyCode :'?',
	    pker->wVirtualScanCode,
	    LOBYTE(pker->uChar.AsciiChar), LOBYTE(pker->uChar.AsciiChar),
	    isprint(pker->uChar.AsciiChar) ? pker->uChar.AsciiChar :'?',
	    pker->dwControlKeyState, fDoPost);
    OutputDebugString(sz);
#endif /* WIN95_DEAD_KEYS_HACK */

    *pch = *pchPending = NUL;
    g_fJustGotFocus = FALSE;

    /* ignore key up events */
    if (!pker->bKeyDown)
	return FALSE;

    /* ignore some keystrokes */
    switch (pker->wVirtualKeyCode)
    {
    /* modifiers */
    case VK_SHIFT:
    case VK_CONTROL:
    case VK_MENU:   /* Alt key */
	return FALSE;

    default:
	break;
    }

#ifdef WIN95_DEAD_KEYS_HACK
    if (fDoPost  &&  uDeadVirtKey != 0)
    {
	sprintf(sz, "sending vk = 0x%2x, sc = 0x%2x\n",
		pker->wVirtualKeyCode, pker->wVirtualScanCode);
	OutputDebugString(sz);

	PostMessage(g_hwndKbd, WM_SENDDEADKEY,
		    MAKELONG(MAKEWORD(uDeadVirtKey, uDeadScan), uDeadCtrlState),
		    MAKELONG(MAKEWORD(pker->wVirtualKeyCode,
				      pker->wVirtualScanCode),
			     pker->dwControlKeyState));
    }
#endif /* WIN95_DEAD_KEYS_HACK */

#if 0
    /* If AltGr has been pressed, remove it.  */
    if ((pker->dwControlKeyState & ALT_GR) == ALT_GR)
	pker->dwControlKeyState &= ~ ALT_GR;
#endif

#if 0
    /* If CapsLock is on on Win95, digits will be shifted by default;
     * e.g., pressing '1' will give '!'.  Feh. */
    if ((pker->dwControlKeyState & CAPSLOCK_ON) &&
	!(pker->dwControlKeyState & SHIFT_PRESSED) &&
	('0' <= pker->wVirtualKeyCode  &&  pker->wVirtualKeyCode <= '9'))
    {
	pker->uChar.AsciiChar = NUL;
    }
#endif

#if 0
    if ((*pch = pker->uChar.AsciiChar) != NUL)
	return TRUE;
#endif

    /* special cases */
    if ((nModifs & CTRL) != 0  &&  (nModifs & ~CTRL) == 0
	&&  pker->uChar.AsciiChar == NUL)
    {
	/* Ctrl-6 is Ctrl-^ */
	if (pker->wVirtualKeyCode == '6')
	{
	    *pch = Ctrl('^');
	    return TRUE;
	}
	/* Ctrl-2 is Ctrl-@ */
	else if (pker->wVirtualKeyCode == '2')
	{
	    *pch = NUL;
	    return TRUE;
	}
    }

    /* Shift-TAB */
    if (pker->wVirtualKeyCode == VK_TAB  &&  (nModifs & SHIFT_PRESSED))
    {
	*pch = K_NUL;
	*pchPending = '\017';
	return TRUE;
    }

    for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]);  --i >= 0;  )
    {
	if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
	{
	    if (nModifs == 0)
		*pch = VirtKeyMap[i].chAlone;
	    else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
		*pch = VirtKeyMap[i].chShift;
	    else if ((nModifs & CTRL) != 0  &&	(nModifs & ~CTRL) == 0)
		*pch = VirtKeyMap[i].chCtrl;
	    else if ((nModifs & ALT) != 0  &&  (nModifs & ~ALT) == 0)
		*pch = VirtKeyMap[i].chAlt;

	    if (*pch != 0)
	    {
		if (VirtKeyMap[i].fAnsiKey)
		{
		    *pchPending = *pch;
		    *pch = K_NUL;
		}

		return TRUE;
	    }
	}
    }

    i = win32_kbd_patch_key(pker);

    if (i < 0)
    {
	*pch = NUL;

#ifdef WIN95_DEAD_KEYS_HACK
	if (fDoPost)
	{
	    uDeadVirtKey = pker->wVirtualKeyCode;
	    uDeadScan = pker->wVirtualScanCode;
	    uDeadCtrlState = pker->dwControlKeyState;
	}
#endif /* WIN95_DEAD_KEYS_HACK */
    }
    else
    {
#ifdef WIN95_DEAD_KEYS_HACK
	uDeadVirtKey = uDeadScan = uDeadCtrlState = 0;
#endif /* WIN95_DEAD_KEYS_HACK */
	*pch = (i > 0)	?  pker->uChar.AsciiChar  :  NUL;
    }

    return (*pch != NUL);
}

#pragma optimize("", on)


#ifdef USE_MOUSE

static int g_fMouseAvail = FALSE;   /* mouse present */
static int g_fMouseActive = FALSE;  /* mouse enabled */
static int g_nMouseClick = -1;	    /* mouse status */
static int g_xMouse;		    /* mouse x coordinate */
static int g_yMouse;		    /* mouse y coordinate */

/*
 * Enable or disable mouse input
 */
    void
mch_setmouse(
    int on)
{
    DWORD cmodein;

    if (! g_fMouseAvail)
	return;

    g_fMouseActive = on;
    GetConsoleMode(g_hConIn, &cmodein);

    if (g_fMouseActive)
	cmodein |= ENABLE_MOUSE_INPUT;
    else
	cmodein &= ~ENABLE_MOUSE_INPUT;

    SetConsoleMode(g_hConIn, cmodein);
}


/*
 * Decode a MOUSE_EVENT.  If it's a valid event, return MOUSE_LEFT,
 * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
 * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
 * or a MOUSE_LEFT, _MIDDLE, or _RIGHT.  We encode the button type,
 * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
 * and we return the mouse position in g_xMouse and g_yMouse.
 *
 * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
 * MOUSE_DRAGs and one MOUSE_RELEASE.  MOUSE_RELEASE will be followed only
 * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
 *
 * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
 * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
 *
 * Windows will send us MOUSE_MOVED notifications whenever the mouse
 * moves, even if it stays within the same character cell.  We ignore
 * all MOUSE_MOVED messages if the position hasn't really changed, and
 * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
 * we're only interested in MOUSE_DRAG).
 *
 * All of this is complicated by the code that fakes MOUSE_MIDDLE on
 * 2-button mouses by pressing the left & right buttons simultaneously.
 * In practice, it's almost impossible to click both at the same time,
 * so we need to delay a little.  Also, we tend not to get MOUSE_RELEASE
 * in such cases, if the user is clicking quickly.
 */
    static BOOL
decode_mouse_event(
    MOUSE_EVENT_RECORD* pmer)
{
    static int s_nOldButton = -1;
    static int s_nOldMouseClick = -1;
    static int s_xOldMouse = -1;
    static int s_yOldMouse = -1;
    static linenr_t s_old_topline = 0;
    static int s_cClicks = 1;
    static BOOL s_fReleased = TRUE;
    static s_dwLastClickTime = 0;
    static BOOL s_fNextIsMiddle = FALSE;

    const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
    const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
    const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
    const DWORD LEFT_RIGHT = LEFT | RIGHT;

    int nButton;

    if (! g_fMouseAvail  ||  ! g_fMouseActive)
    {
	g_nMouseClick = -1;
	return FALSE;
    }

    /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
    if (g_fJustGotFocus)
    {
	g_fJustGotFocus = FALSE;
	return FALSE;
    }

    /* unprocessed mouse click? */
    if (g_nMouseClick != -1)
	return TRUE;

    nButton = -1;
    g_xMouse = pmer->dwMousePosition.X;
    g_yMouse = pmer->dwMousePosition.Y;

    if (pmer->dwEventFlags == MOUSE_MOVED)
    {
	/* ignore MOUSE_MOVED events if (x, y) hasn't changed.	(We get these
	 * events even when the mouse moves only within a char cell.) */
	if (s_xOldMouse == g_xMouse  &&  s_yOldMouse == g_yMouse)
	    return FALSE;
    }

    /* If no buttons are pressed... */
    if (pmer->dwButtonState == 0)
    {
	/* If the last thing returned was MOUSE_RELEASE, ignore this */
	if (s_fReleased)
	    return FALSE;

	nButton = MOUSE_RELEASE;
	s_fReleased = TRUE;
    }
    else    /* one or more buttons pressed */
    {
	const int cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);

	/* on a 2-button mouse, hold down left and right buttons
	 * simultaneously to get MIDDLE. */

	if (cButtons == 2  &&  s_nOldButton != MOUSE_DRAG)
	{
	    DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);

	    /* if either left or right button only is pressed, see if the
	     * the next mouse event has both of them pressed */
	    if (dwLR == LEFT  ||  dwLR	== RIGHT)
	    {
		for (;;)
		{
		    /* wait a short time for next input event */
		    if (WaitForSingleObject(g_hConIn, p_mouset / 3)
			!= WAIT_OBJECT_0)
			break;
		    else
		    {
			DWORD cRecords = 0;
			INPUT_RECORD ir;
			MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;

			PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);

			if (cRecords == 0  ||  ir.EventType != MOUSE_EVENT
			    ||	!(pmer2->dwButtonState & LEFT_RIGHT))
			    break;
			else
			{
			    if (pmer2->dwEventFlags != MOUSE_MOVED)
			    {
				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

				return decode_mouse_event(pmer2);
			    }
			    else if (s_xOldMouse == pmer2->dwMousePosition.X &&
				     s_yOldMouse == pmer2->dwMousePosition.Y)
			    {
				/* throw away spurious mouse move */
				ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

				/* are there any more mouse events in queue? */
				PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);

				if (cRecords==0 || ir.EventType != MOUSE_EVENT)
				    break;
			    }
			    else
				break;
			}
		    }
		}
	    }
	}

	if (s_fNextIsMiddle)
	{
	    nButton = (pmer->dwEventFlags == MOUSE_MOVED)
		? MOUSE_DRAG : MOUSE_MIDDLE;
	    s_fNextIsMiddle = FALSE;
	}
	else if (cButtons == 2	&&
	    ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
	{
	    nButton = MOUSE_MIDDLE;

	    if (! s_fReleased  &&  pmer->dwEventFlags != MOUSE_MOVED)
	    {
		s_fNextIsMiddle = TRUE;
		nButton = MOUSE_RELEASE;
	    }
	}
	else if ((pmer->dwButtonState & LEFT) == LEFT)
	    nButton = MOUSE_LEFT;
	else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
	    nButton = MOUSE_MIDDLE;
	else if ((pmer->dwButtonState & RIGHT) == RIGHT)
	    nButton = MOUSE_RIGHT;

	if (! s_fReleased  && ! s_fNextIsMiddle
	    &&	nButton != s_nOldButton  &&  s_nOldButton != MOUSE_DRAG)
	    return FALSE;

	s_fReleased = s_fNextIsMiddle;
    }

    if (pmer->dwEventFlags == 0  ||  pmer->dwEventFlags == DOUBLE_CLICK)
    {
	/* button pressed or released, without mouse moving */
	if (nButton != -1  &&  nButton != MOUSE_RELEASE)
	{
	    DWORD dwCurrentTime = GetTickCount();

	    if (s_xOldMouse != g_xMouse  ||  s_yOldMouse != g_yMouse
		||  s_nOldButton != nButton
		||  s_old_topline != curwin->w_topline
		||  (int) (dwCurrentTime - s_dwLastClickTime) > p_mouset)
	    {
		s_cClicks = 1;
	    }
	    else if (++s_cClicks > 4)
	    {
		s_cClicks = 1;
	    }

	    s_dwLastClickTime = dwCurrentTime;
	}
    }
    else if (pmer->dwEventFlags == MOUSE_MOVED)
    {
	if (nButton != -1  &&  nButton != MOUSE_RELEASE)
	    nButton = MOUSE_DRAG;

	s_cClicks = 1;
    }

    if (nButton == -1)
	return FALSE;

    if (nButton != MOUSE_RELEASE)
	s_nOldButton = nButton;

    g_nMouseClick = nButton;

    if (pmer->dwControlKeyState & SHIFT_PRESSED)
	g_nMouseClick |= MOUSE_SHIFT;
    if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
	g_nMouseClick |= MOUSE_CTRL;
    if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED))
	g_nMouseClick |= MOUSE_ALT;

    /* only pass on interesting (i.e., different) mouse events */
    if (s_xOldMouse == g_xMouse  &&  s_yOldMouse == g_yMouse
	&&  s_nOldMouseClick == g_nMouseClick)
    {
	g_nMouseClick = -1;
	return FALSE;
    }

    g_nMouseClick |= 0x20;

    s_xOldMouse = g_xMouse;
    s_yOldMouse = g_yMouse;
    s_old_topline = curwin->w_topline;
    s_nOldMouseClick = g_nMouseClick;

    if (nButton != MOUSE_DRAG  &&  nButton != MOUSE_RELEASE)
	SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);

    return TRUE;
}


#endif /* USE_MOUSE */


/*
 * Wait until console input from keyboard or mouse is available,
 * or the time is up
 */
    static int
WaitForChar(
    long msec)
{
    DWORD dwNow, dwEndTime;

    if (msec <= 0)
	msec = 2;   /* very short time */

    dwEndTime = GetTickCount() + msec;

    /* We need to loop until the end of the time period, because
     * we might get multiple unusable mouse events in that time.
     */
    while ((dwNow = GetTickCount())  <	dwEndTime)
    {
	if (WaitForSingleObject(g_hConIn, dwEndTime - dwNow) == WAIT_OBJECT_0)
	{
	    INPUT_RECORD ir;
	    DWORD cRecords = 0;

	    PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);

	    if (cRecords > 0)
	    {
		if (ir.EventType == KEY_EVENT  &&  ir.Event.KeyEvent.bKeyDown)
		{
		    char_u ch, ch2;

		    return decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
					    FALSE);
		}
#ifdef USE_CLIPBOARD
		else if (ir.EventType == FOCUS_EVENT)
		{
		    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

		    g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
		    /* TRACE("WaitForChar: Focus %d\n", g_fJustGotFocus); */
		    clip_mch_lose_focus();
		}
#endif
		else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
		{
		    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

		    set_winsize(Rows, Columns, FALSE);
		}
#ifdef USE_MOUSE
		else if (ir.EventType == MOUSE_EVENT)
		{
		    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

		    if (decode_mouse_event(&ir.Event.MouseEvent))
			return TRUE;
		}
#endif /* USE_MOUSE */
		else
		{
		    /* Discard it, it's an insignificant event */
		    ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
		}
	    }
	}
    }

    return FALSE;
}


static char_u g_chPending = NUL;


/*
 * Is there a pending keystroke or mouse event?
 */
    static BOOL
kbhit()
{
    if (g_chPending != NUL
#ifdef USE_MOUSE
	|| g_nMouseClick != -1
#endif /* USE_MOUSE */
	)
	return TRUE;

    for ( ; ; )
    {
	INPUT_RECORD ir;
	DWORD cRecords = 0;

	PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);

	if (cRecords == 0)
	    return FALSE;
	else
	{
	    if (ir.EventType == KEY_EVENT  &&  ir.Event.KeyEvent.bKeyDown)
	    {
		char_u ch, ch2;

		return decode_key_event(&ir.Event.KeyEvent, &ch, &ch2, FALSE);
	    }
#ifdef USE_CLIPBOARD
	    else if (ir.EventType == FOCUS_EVENT)
	    {
		ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

		g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
		/* TRACE("kbhit: Focus %d\n", g_fJustGotFocus); */
		clip_mch_lose_focus();
	    }
#endif
	    else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
	    {
		ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

		set_winsize(Rows, Columns, FALSE);
	    }
#ifdef USE_MOUSE
	    else if (ir.EventType == MOUSE_EVENT)
	    {
		ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

		if (decode_mouse_event(&ir.Event.MouseEvent))
		    return TRUE;
	    }
#endif /* USE_MOUSE */
	    else
	    {
		/* Discard it, it's an insignificant event */
		ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
	    }
	}
    }
}


/*
 * Get a keystroke or a mouse event
 */
    static char_u
tgetch()
{
    char_u ch;

    if (g_chPending != NUL)
    {
	ch = g_chPending;
	g_chPending = NUL;
	return ch;
    }

    for ( ; ; )
    {
	INPUT_RECORD ir;
	DWORD cRecords = 0;

	ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);

	if (ir.EventType == KEY_EVENT)
	{
	    if (decode_key_event(&ir.Event.KeyEvent, &ch, &g_chPending, TRUE))
		return ch;
	}
#ifdef USE_CLIPBOARD
	else if (ir.EventType == FOCUS_EVENT)
	{
	    g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
	    /* TRACE("tgetch: Focus %d\n", g_fJustGotFocus); */
	    clip_mch_lose_focus();
	}
#endif
	else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
	{
	    set_winsize(Rows, Columns, FALSE);
	}
#ifdef USE_MOUSE
	else if (ir.EventType == MOUSE_EVENT)
	{
	    if (decode_mouse_event(&ir.Event.MouseEvent))
		return 0;
	}
#endif /* USE_MOUSE */
    }
}


/*
 * mch_inchar(): low-level input funcion.
 * Get one or more characters from the keyboard or the mouse.
 * If time == 0, do not wait for characters.
 * If time == n, wait a short time for characters.
 * If time == -1, wait forever for characters.
 * Returns the number of characters read into buf.
 */
    int
mch_inchar(
    char_u *buf,
    int maxlen,
    long time)
{
    int len = 0;
    int c;

    if (time >= 0)
    {
	if (! WaitForChar(time))     /* no character available */
	    return 0;
    }
    else    /* time == -1, wait forever */
    {
	/* If there is no character available within 2 seconds (default),
	 * write the autoscript file to disk */
	if (WaitForChar(p_ut) == 0)
	    updatescript(0);
    }

    /*
     * Try to read as many characters as there are.
     */

    --maxlen;		       /* may get two chars at once */

    /* we will get at least one key. Get more if they are available. */
    g_fCBrkPressed = FALSE;

#ifdef MCH_WRITE_DUMP
    if (fdDump)
	fputc('[', fdDump);
#endif /* MCH_WRITE_DUMP */

    while ((len == 0 || kbhit())  &&  len < maxlen)
    {
#ifdef USE_MOUSE
	if (g_nMouseClick != -1  &&  maxlen >= 5-1)
	{
# ifdef MCH_WRITE_DUMP
	    if (fdDump)
		fprintf(fdDump, "{%02x @ %d, %d}",
			g_nMouseClick, g_xMouse, g_yMouse);
# endif /* MCH_WRITE_DUMP */

	    len = 5;
	    *buf++ = ESC + 128;
	    *buf++ = 'M';
	    *buf++ = g_nMouseClick;
	    *buf++ = g_xMouse + '!';
	    *buf++ = g_yMouse + '!';
	    g_nMouseClick = -1;

	}
	else
#endif /* USE_MOUSE */
	{
	    if ((c = tgetch()) == Ctrl('C'))
		g_fCBrkPressed = TRUE;

#ifdef USE_MOUSE
	    if (g_nMouseClick == -1)
#endif /* USE_MOUSE */
	    {
		*buf++ = c;
		len++;

#ifdef MCH_WRITE_DUMP
		if (fdDump)
		    fputc(c, fdDump);
#endif /* MCH_WRITE_DUMP */

	    }
	}
    }

#ifdef MCH_WRITE_DUMP
    if (fdDump)
    {
	fputs("]\n", fdDump);
	fflush(fdDump);
    }
#endif /* MCH_WRITE_DUMP */

    return len;
}


static char g_szOrigTitle[256];
static int  g_fWindInitCalled = FALSE;
static CONSOLE_CURSOR_INFO g_cci;
static DWORD g_cmodein = 0;
static DWORD g_cmodeout = 0;

/*
 * Because of a bug in the Windows 95 Console, we need to set the screen size
 * back when switching screens.
 */
static int g_nOldRows = 0;
static int g_nOldColumns = 0;

/*
 * platform-specific initialization
 */
    void
mch_windinit()
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    extern int _fmode;

    PlatformId();

    _fmode = O_BINARY;		/* we do our own CR-LF translation */
    flushbuf();

    /* Obtain handles for the standard Console I/O devices */
    if (read_cmd_fd == 0)
	g_hConIn =  GetStdHandle(STD_INPUT_HANDLE);
    else
	/*
	 * TODO: Should get something else than stdin, but this doesn't seem
	 * to work:
	 */
	g_hConIn =  GetStdHandle(STD_OUTPUT_HANDLE);
    g_hSavOut = GetStdHandle(STD_OUTPUT_HANDLE);
    g_hCurOut = g_hSavOut;

    /* Get current text attributes */
    GetConsoleScreenBufferInfo(g_hSavOut, &csbi);
    g_attrCurrent = g_attrDefault = csbi.wAttributes;
    GetConsoleCursorInfo(g_hSavOut, &g_cci);

    GetConsoleMode(g_hConIn,  &g_cmodein);
    GetConsoleMode(g_hSavOut, &g_cmodeout);

    GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle));
    ui_get_winsize();

    g_nOldRows = Rows;
    g_nOldColumns = Columns;

#ifdef MCH_WRITE_DUMP
    fdDump = fopen("dump", "wt");

    if (fdDump)
    {
	time_t t;

	time(&t);
	fputs(ctime(&t), fdDump);
	fflush(fdDump);
    }
#endif /* MCH_WRITE_DUMP */

    g_fWindInitCalled = TRUE;

#ifdef USE_MOUSE
    g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
#endif

#ifdef USE_CLIPBOARD
    clip_init(TRUE);

    /*
     * Vim's own clipboard format recognises whether the text is char, line, or
     * rectangular block.  Only useful for copying between two vims.
     */
    clipboard.format = RegisterClipboardFormat("VimClipboard");
#endif

#ifdef WIN95_DEAD_KEYS_HACK
    keyboard_init();
#endif

    /* This will be NULL on anything but NT 4.0 */
    s_pfnGetConsoleKeyboardLayoutName =
	(PFNGCKLN) GetProcAddress(GetModuleHandle("kernel32.dll"),
				  "GetConsoleKeyboardLayoutNameA");
    /*
     * We don't really want to jump to our own screen yet; do that after
     * starttermcap().	This flashes the window, sorry about that, but
     * otherwise "vim -r" doesn't work.
     */
    g_hCurOut = g_hSavOut;
    SetConsoleActiveScreenBuffer(g_hCurOut);
}


/*
 * Shut down and exit with status `r'
 * Careful: mch_windexit() may be called before mch_windinit()!
 */
    void
mch_windexit(
    int r)
{
    stoptermcap();
    outchar('\r');
    outchar('\n');
    flushbuf();

    if (g_fWindInitCalled)
	settmode(TMODE_COOK);

    if (g_hConOut != INVALID_HANDLE_VALUE)
    {
	(void) CloseHandle(g_hConOut);

	if (g_hSavOut != INVALID_HANDLE_VALUE)
	{
	    SetConsoleTextAttribute(g_hSavOut, g_attrDefault);
	    SetConsoleCursorInfo(g_hSavOut, &g_cci);
	}
    }

    ml_close_all(TRUE);		/* remove all memfiles */

    if (g_fWindInitCalled)
    {
#ifdef WIN95_DEAD_KEYS_HACK
	keyboard_exit();
#endif

	mch_restore_title(3);

#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
	    time_t t;

	    time(&t);
	    fputs(ctime(&t), fdDump);
	    fclose(fdDump);
	}
	fdDump = NULL;
#endif /* MCH_WRITE_DUMP */
    }

    exit(r);
}


/*
 * Do we have an interactive window?
 */
    int
mch_check_win(
    int argc,
    char **argv)
{
    char temp [ 256 ];
    char * p;

    GetModuleFileName( NULL, temp, 255 );
    p = strrchr(temp, '.');
    if (p != NULL)
	*p = '\0';
    exe_name = strdup(temp);	/* remember the name of the executable */

    if (isatty(1))
	return OK;
    return FAIL;
}


/*
 * Return TRUE if the input comes from a terminal, FALSE otherwise.
 */
    int
mch_input_isatty()
{
    if (isatty(read_cmd_fd))
	return TRUE;
    return FALSE;
}


/*
 * Turn a file name into its canonical form.  Replace slashes with backslashes.
 * This used to replace backslashes with slashes, but that caused problems
 * when using the file name as a command.  We can't have a mix of slashes and
 * backslashes, because comparing file names will not work correctly.  The
 * commands that use file names should be prepared to handle the backslashes.
 */
    static void
canonicalize_filename(
    char* pszName)
{
    if (pszName == NULL)
	return;

    for ( ; *pszName;  pszName++)
    {
	if (*pszName == '/')
	    *pszName = '\\';
#ifdef DOWNCASE_FILENAMES
	else
	    *pszName = TO_LOWER(*pszName);
#endif /* DOWNCASE_FILENAMES */
    }
}


/*
 * fname_case(): Set the case of the file name, if it already exists.
 */
    void
fname_case(
    char_u *name)
{
#ifdef DOWNCASE_FILENAMES
    canonicalize_filename(name);
#else /* !DOWNCASE_FILENAMES */
    char szTrueName[_MAX_PATH + 1];
    char *psz, *pszPrev;
    const int len = (name != NULL)  ?  STRLEN(name)  :	0;

    if (len == 0)
	return;

    STRCPY(szTrueName, name);
    STRCAT(szTrueName, "\\");	/* sentinel */

    for (psz = szTrueName;  *psz != NUL;  psz++)
	if (*psz == '/')
	    *psz = '\\';

    psz = pszPrev = szTrueName;

    /* Skip leading \\ in UNC name or drive letter */
    if (len > 2   &&  ((psz[0] == '\\'	&&  psz[1] == '\\')
		       || (isalpha(psz[0])  &&	psz[1] == ':')))
    {
	psz = pszPrev = szTrueName + 2;
    }

    while (*psz != NUL)
    {
	WIN32_FIND_DATA	fb;
	HANDLE		hFind;

	while (*psz != '\\')
	    psz++;
	*psz = NUL;

	if ((hFind = FindFirstFile(szTrueName, &fb)) != INVALID_HANDLE_VALUE)
	{
	    /* avoid ".." and ".", etc */
	    if (_stricoll(pszPrev, fb.cFileName) == 0)
		STRCPY(pszPrev, fb.cFileName);
	    FindClose(hFind);
	}

	*psz++ = '\\';
	pszPrev = psz;
    }

    *--psz = NUL;   /* remove sentinel */

    STRCPY(name, szTrueName);
#endif /* !DOWNCASE_FILENAMES */
}


/*
 * mch_settitle(): set titlebar of our window
 * Can the icon also be set?
 */
    void
mch_settitle(
    char_u *title,
    char_u *icon)
{
    if (title != NULL)
#ifdef USE_GUI_WIN32
	gui_mch_settitle(title, icon);
#else
	SetConsoleTitle(title);
#endif
}


/*
 * Restore the window/icon title.
 * which is one of:
 *  1: Just restore title
 *  2: Just restore icon (which we don't have)
 *  3: Restore title and icon (which we don't have)
 */
    void
mch_restore_title(
    int which)
{
    mch_settitle((which & 1) ? g_szOrigTitle : NULL, NULL);
}


/*
 * Return TRUE if we can restore the title (we can)
 */
    int
mch_can_restore_title()
{
    return TRUE;
}


/*
 * Return TRUE if we can restore the icon (we can't)
 */
    int
mch_can_restore_icon()
{
    return FALSE;
}


/*
 * Insert user name in s[len].
 */
    int
mch_get_user_name(
    char_u *s,
    int len)
{
    char szUserName[MAX_COMPUTERNAME_LENGTH + 1];
    DWORD cch = sizeof szUserName;

    if (GetUserName(szUserName, &cch))
    {
	STRNCPY(s, szUserName, len);
	return OK;
    }
    s[0] = NUL;
    return FAIL;
}


/*
 * Insert host name in s[len].
 */
    void
mch_get_host_name(
    char_u *s,
    int len)
{
    char szHostName[MAX_COMPUTERNAME_LENGTH + 1];
    DWORD cch = sizeof szHostName;

    if (GetComputerName(szHostName, &cch))
    {
	STRCPY(s, "PC ");
	STRNCPY(s + 3, szHostName, len - 3);
    }
    else
	STRNCPY(s, "PC (Win32 Vim)", len);
}


/*
 * return process ID
 */
    long
mch_get_pid()
{
    return (long) GetCurrentProcessId();
}


/*
 * Get name of current directory into buffer 'buf' of length 'len' bytes.
 * Return OK for success, FAIL for failure.
 */
    int
mch_dirname(
    char_u *buf,
    int len)
{
    return (getcwd(buf, len) != NULL ? OK : FAIL);
}


/*
 * Get absolute file name into buffer 'buf' of length 'len' bytes,
 * turning all '/'s into '\\'s and getting the correct case of each
 * component of the file name.	Return OK or FAIL.
 */
    int
mch_FullName(
    char_u *fname,
    char_u *buf,
    int len,
    int force)
{
    int nResult = FAIL;

    if (fname == NULL)		/* always fail */
	return FAIL;

    if (_fullpath(buf, fname, len) == NULL)
    {
	/* failed, use the relative path name */
	STRNCPY(buf, fname, len);
    }
    else
	nResult = OK;

    fname_case(buf);

    return nResult;
}


/*
 * return TRUE if `fname' is an absolute path name
 */
    int
mch_isFullName(
    char_u *fname)
{
    char szName[_MAX_PATH];

    mch_FullName(fname, szName, _MAX_PATH, FALSE);

#ifdef DOWNCASE_FILENAMES
    return _stricoll(fname, szName) == 0;
#else /* !DOWNCASE_FILENAMES */
    return strcoll(fname, szName) == 0;
#endif /* !DOWNCASE_FILENAMES */
}

/*
 * Replace all slashes by backslashes.
 * This used to be the other way around, but MS-DOS sometimes has problems
 * with slashes (e.g. in a command name).  We can't have mixed slashes and
 * backslashes, because comparing file names will not work correctly.  The
 * commands that use a file name should try to avoid the need to type a
 * backslash twice.
 */
    void
slash_adjust(p)
    char_u  *p;
{
    while (*p)
    {
	if (*p == '/')
	    *p = '\\';
	++p;
    }
}


/*
 * get file permissions for `name'
 * -1 : error
 * else FILE_ATTRIBUTE_* defined in winnt.h
 */
    long
mch_getperm(
    char_u *name)
{
    return GetFileAttributes(name);
}


/*
 * set file permission for `name' to `perm'
 */
    int
mch_setperm(
    char_u *name,
    long perm)
{
    perm |= FILE_ATTRIBUTE_ARCHIVE;	/* file has changed, set archive bit */
    return SetFileAttributes(name, perm) ? OK : FAIL;
}


/*
 * return TRUE if "name" is a directory
 * return FALSE if "name" is not a directory or upon error
 */
    int
mch_isdir(
    char_u *name)
{
    int f = mch_getperm(name);

    if (f == -1)
	return FALSE;		    /* file does not exist at all */

    return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
}


/*
 * handler for ctrl-break, ctrl-c interrupts, and fatal events.
 */
    static BOOL WINAPI
handler_routine(
    DWORD dwCtrlType)
{
    switch (dwCtrlType)
    {
    case CTRL_C_EVENT:
	g_fCtrlCPressed = TRUE;
	return TRUE;

    case CTRL_BREAK_EVENT:
	g_fCBrkPressed	= TRUE;
	return TRUE;

    /* fatal events: shut down gracefully */
    case CTRL_CLOSE_EVENT:
    case CTRL_LOGOFF_EVENT:
    case CTRL_SHUTDOWN_EVENT:
	windgoto((int)Rows - 1, 0);
	sprintf((char *)IObuff, "Vim: Caught %s event\n",
		(dwCtrlType == CTRL_CLOSE_EVENT ? "close"
		 : dwCtrlType == CTRL_LOGOFF_EVENT ? "logoff" : "shutdown"));

#ifdef DEBUG
	OutputDebugString(IObuff);
#endif /* DEBUG */

	preserve_exit();	/* output IObuff, preserve files and exit */

	return TRUE;		/* not reached */

    default:
	return FALSE;
    }
}


/*
 * set the tty in (raw) ? "raw" : "cooked" mode
 */
    void
mch_settmode(
    int tmode)
{
    DWORD cmodein;
    DWORD cmodeout;

    GetConsoleMode(g_hConIn,  &cmodein);
    GetConsoleMode(g_hCurOut, &cmodeout);

    if (tmode == TMODE_RAW)
    {
	cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
		     ENABLE_ECHO_INPUT);
	cmodein |= (
#ifdef USE_MOUSE
	    (g_fMouseActive ? ENABLE_MOUSE_INPUT : 0) |
#endif
	    ENABLE_WINDOW_INPUT);

	SetConsoleMode(g_hConIn, cmodein);

	cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
	SetConsoleMode(g_hCurOut, cmodeout);
	SetConsoleCtrlHandler(handler_routine, TRUE);
    }
    else /* cooked */
    {
	cmodein =  g_cmodein;
	cmodeout = g_cmodeout;
	SetConsoleMode(g_hConIn,  g_cmodein);
	SetConsoleMode(g_hCurOut, g_cmodeout);

	SetConsoleCtrlHandler(handler_routine, FALSE);
    }

#ifdef MCH_WRITE_DUMP
    if (fdDump)
    {
	fprintf(fdDump, "mch_settmode(%s, CurOut = %s, in = %x, out = %x)\n",
		tmode == TMODE_RAW ? "raw" :
				    tmode == TMODE_COOK ? "cooked" : "normal",
		(g_hCurOut == g_hSavOut ? "Sav" : "Con"),
		cmodein, cmodeout);
	fflush(fdDump);
    }
#endif /* MCH_WRITE_DUMP */
}


/*
 * Get the size of the current window in `Rows' and `Columns'
 */
    int
mch_get_winsize()
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;

    if (GetConsoleScreenBufferInfo(g_hCurOut, &csbi))
    {
	Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
	Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
    }
    else
    {
	Rows = 25;
	Columns = 80;
    }

    if (Columns < MIN_COLUMNS  ||  Rows < MIN_ROWS + 1)
    {
	/* these values are overwritten by termcap size or default */
	Rows = 25;
	Columns = 80;
    }

    check_winsize();
    set_scroll_region(0, 0, Columns - 1, Rows - 1);

    return OK;
}


/*
 * Set a console window to `xSize' * `ySize'
 */
    static void
ResizeConBufAndWindow(
    HANDLE hConsole,
    int xSize,
    int ySize)
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;	/* hold current console buffer info */
    SMALL_RECT	    srWindowRect;	/* hold the new console size */
    COORD	    coordScreen;

#ifdef MCH_WRITE_DUMP
    if (fdDump)
    {
	fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
	fflush(fdDump);
    }
#endif

    GetConsoleScreenBufferInfo(hConsole, &csbi);

    /* get the largest size we can size the console window to */
    coordScreen = GetLargestConsoleWindowSize(hConsole);

    /* define the new console window size and scroll position */
    srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
    srWindowRect.Right =  (SHORT) (min(xSize, coordScreen.X) - 1);
    srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);

    /* define the new console buffer size */
    coordScreen.X = xSize;
    coordScreen.Y = ySize;

    if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect))
    {
#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
	    fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
		    GetLastError());
	    fflush(fdDump);
	}
#endif
    }

    if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
    {
#ifdef MCH_WRITE_DUMP
	if (fdDump)
	{
	    fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
		    GetLastError());
	    fflush(fdDump);
	}
#endif
    }

}


/*
 * Set the console window to `Rows' * `Columns'
 */
    void
mch_set_winsize()
{
    COORD coordScreen = GetLargestConsoleWindowSize(g_hCurOut);

    /* Clamp Rows and Columns to reasonable values */
    if (Rows > coordScreen.Y)
	Rows = coordScreen.Y;
    if (Columns > coordScreen.X)
	Columns = coordScreen.X;

    ResizeConBufAndWindow(g_hCurOut, Columns, Rows);
    set_scroll_region(0, 0, Columns - 1, Rows - 1);
}


/*
 * We have no job control, so fake it by starting a new shell.
 */
    void
mch_suspend()
{
    suspend_shell();
}


/*
 * Either execute a command by calling the shell or start a new shell
 */
    int
mch_call_shell(
    char_u *cmd,
    int options)	    /* SHELL_FILTER if called by do_filter() */
			    /* SHELL_COOKED if term needs cooked mode */
{
    int	    x;
    int	    stopped_termcap_mode = FALSE;

    flushbuf();

#ifndef USE_GUI_WIN32
    /*
     * ALWAYS switch to non-termcap mode, otherwise ":r !ls" may crash.
     */
    if (g_hCurOut == g_hConOut)
    {
	termcap_mode_end();
	stopped_termcap_mode = TRUE;
    }
#endif /* USE_GUI_WIN32 */

#ifdef MCH_WRITE_DUMP
    if (fdDump)
    {
	fprintf(fdDump, "mch_call_shell(\"%s\", %d)\n", cmd, options);
	fflush(fdDump);
    }
#endif

    signal(SIGINT, SIG_IGN);	/* we don't want to be killed here by Ctrl-C*/
    signal(SIGBREAK, SIG_IGN);	/* Nor by Ctrl-Break */

    if (options & SHELL_COOKED)
	settmode(TMODE_COOK);	/* set to normal mode */

    if (cmd == NULL)
    {
	x = system(p_sh);
    }
    else
    {
	/* we use "command" or "cmd" to start the shell; slow but easy */
	char newcmd[CMDBUFFSIZE + 100];

	sprintf(newcmd, "%s %s %s", p_sh,
#ifdef USE_GUI_WIN32
		/*
		 * Use "/k" instead of "/c", to keep the window displayed
		 * after the command has been executed.
		 */
		!(options & SHELL_DOOUT) && STRCMP(p_shcf, "/c") == 0
			    ? "/k" :
#endif
					    p_shcf, cmd);
	x = system(newcmd);
    }

    settmode(TMODE_RAW);	    /* set to raw mode */

    if (x && !expand_interactively)
    {
	smsg("%d returned", x);
	msg_putchar('\n');
    }
    resettitle();

    signal(SIGINT, SIG_DFL);
    signal(SIGBREAK, SIG_DFL);

    if (stopped_termcap_mode)
	termcap_mode_start();

    return (x ? FAIL : OK);
}


/*
 * Does `s' contain a wildcard?
 */
    int
mch_has_wildcard(
    char_u *s)
{
    for ( ;  *s;  ++s)
	if (*s == '?' || *s == '*' || *s == '$')
	    return TRUE;
    return FALSE;
}


/*
 * Copy a string, forcing it to lowercase if DOWNCASE_FILENAMES is defined
 */
#ifdef DOWNCASE_FILENAMES
    static void
strlowcpy(
    char *d,
    char *s)
{
    while (*s)
	*d++ = TO_LOWER(*s++);
    *d = NUL;
}
#else /* !DOWNCASE_FILENAMES */
# define strlowcpy(d, s) STRCPY(d, s)
#endif /* !DOWNCASE_FILENAMES */


/*
 * comparison function for qsort in expandpath
 */
    static int
#ifdef __BORLANDC__
_RTLENTRYF
#endif
pstrcmp(
    const void *a,
    const void *b)
{
    return (_stricoll(* (char **) a, * (char **) b));
}


/*
 * Recursively build up a list of files in "gap" matching the first wildcard
 * in `path'.  Called by expand_wildcards().
 */
    void
mch_expandpath(
    struct growarray	*gap,
    char_u		*path,
    int			flags)
{
    char	    buf[_MAX_PATH];
    char	   *p,
		   *s,
		   *e;
    int		    start_len,
		    c = 1;
    WIN32_FIND_DATA fb;
    HANDLE	    hFind;
    int		    found_one = FALSE;

    start_len = gap->ga_len;

    /*
     * Find the first part in the path name that contains a wildcard.
     * Copy it into `buf', including the preceding characters.
     */
    p = buf;
    s = NULL;
    e = NULL;
    while (*path)
    {
	if (*path == '\\' || *path == ':' || *path == '/')
	{
	    if (e)
		break;
	    else
		s = p;
	}
	if (*path == '*' || *path == '?')
	    e = p;
	*p++ = *path++;
    }
    e = p;
    if (s)
	s++;
    else
	s = buf;

    /* now we have one wildcard component between `s' and `e' */
    *e = NUL;

    /* If we are expanding wildcards, we try both files and directories */
    if ((hFind = FindFirstFile(buf, &fb)) != INVALID_HANDLE_VALUE)
	while (c)
	{
	    strlowcpy(s, fb.cFileName);

	    /*
	     * Ignore "." and "..".
	     * When more follows, this must be a directory.
	     */
	    if ((s[0] != '.'  ||
		    (s[1] != NUL  &&  (s[1] != '.' || s[2] != NUL))) &&
		    (*path == NUL || mch_isdir(buf)))
	    {
		strcat(buf, path);
		if (!mch_has_wildcard(path))
		    addfile(gap, buf, flags);
		else
		    mch_expandpath(gap, buf, flags);
		found_one = TRUE;
	    }

	    c = FindNextFile(hFind, &fb);
	}

    if (!found_one)
    {
	/* not found */
	STRCPY(e, path);

	if (flags & EW_NOTFOUND)
	    addfile(gap, buf, flags);

	return;		   /* unexpanded or empty */
    }

    if (gap->ga_len > start_len)
	qsort(((char_u **)gap->ga_data) + start_len, gap->ga_len - start_len,
						     sizeof(char *), pstrcmp);
    FindClose(hFind);

    return;
}


#ifdef vim_chdir
# undef vim_chdir
#endif

/*
 * The normal _chdir() does not change the default drive.  This one does.
 * Returning 0 implies success; -1 implies failure.
 */
    int
vim_chdir(
    char *path)
{
    if (path[0] == NUL)		/* just checking... */
	return -1;

    if (isalpha(path[0])  &&  path[1] == ':')	/* has a drive name */
    {
	if (_chdrive(TO_UPPER(path[0]) - 'A' + 1) != 0)
	    return -1;		/* invalid drive name */
	path += 2;
    }

    if (*path == NUL)		/* drive name only */
	return 0;

    return chdir(path);	       /* let the normal chdir() do the rest */
}


/*
 * Copy the contents of screen buffer hSrc to the bottom-left corner
 * of screen buffer hDst.
 */
    static void
copy_screen_buffer_text(
    HANDLE hSrc,
    HANDLE hDst)
{
    int	    i, j, nSrcWidth, nSrcHeight, nDstWidth, nDstHeight;
    COORD   coordOrigin;
    DWORD   dwDummy;
    LPSTR   pszOldText;
    CONSOLE_SCREEN_BUFFER_INFO csbiSrc, csbiDst;

#ifdef MCH_WRITE_DUMP
    if (fdDump)
    {
	fprintf(fdDump, "copy_screen_buffer_text(%s, %s)\n",
		(hSrc == g_hSavOut ? "Sav" : "Con"),
		(hDst == g_hSavOut ? "Sav" : "Con"));
	fflush(fdDump);
    }
#endif /* MCH_WRITE_DUMP */

    GetConsoleScreenBufferInfo(hSrc, &csbiSrc);
    nSrcWidth =  csbiSrc.srWindow.Right  - csbiSrc.srWindow.Left + 1;
    nSrcHeight = csbiSrc.srWindow.Bottom - csbiSrc.srWindow.Top  + 1;

    GetConsoleScreenBufferInfo(hDst, &csbiDst);
    nDstWidth =  csbiDst.srWindow.Right  - csbiDst.srWindow.Left + 1;
    nDstHeight = csbiDst.srWindow.Bottom - csbiDst.srWindow.Top  + 1;

    pszOldText = (LPSTR) alloc(nDstHeight * nDstWidth);

    /* clear the top few lines if Src shorter than Dst */
    for (i = 0;  i < nDstHeight - nSrcHeight;  i++)
    {
	for (j = 0;  j < nDstWidth;  j++)
	    pszOldText[i * nDstWidth + j] = ' ';
    }

    /* Grab text off source screen. */
    coordOrigin.X = 0;
    coordOrigin.Y = (SHORT) max(0, csbiSrc.srWindow.Bottom + 1 - nDstHeight);

    for (i = 0;  i < min(nSrcHeight, nDstHeight);  i++)
    {
	LPSTR psz = pszOldText
		     + (i + max(0, nDstHeight - nSrcHeight)) * nDstWidth;

	ReadConsoleOutputCharacter(hSrc, psz, min(nDstWidth, nSrcWidth),
				   coordOrigin, &dwDummy);
	coordOrigin.Y++;

	/* clear the last few columns if Src narrower than Dst */
	for (j = nSrcWidth;  j < nDstWidth;  j++)
	    psz[j] = ' ';
    }

    /* paste Src's text onto Dst */
    coordOrigin.Y = csbiDst.srWindow.Top;
    WriteConsoleOutputCharacter(hDst, pszOldText, nDstHeight * nDstWidth,
				coordOrigin, &dwDummy);
    vim_free(pszOldText);
}


/* keep track of state of original console window */
static SMALL_RECT   g_srOrigWindowRect;
static COORD	    g_coordOrig;
static WORD	    g_attrSave = 0;


/*
 * Start termcap mode by switching to our allocated screen buffer
 */
    static void
termcap_mode_start(void)
{
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    DWORD dwDummy;
    COORD coord;
    WORD wAttr = (WORD) (g_attrSave ? g_attrSave : g_attrCurrent);

    GetConsoleScreenBufferInfo(g_hSavOut, &csbi);
    g_srOrigWindowRect = csbi.srWindow;

    g_coordOrig.X = 0;
    g_coordOrig.Y = csbi.dwCursorPosition.Y;

    if (g_hConOut == INVALID_HANDLE_VALUE)
    {
	/* Create a new screen buffer in which we do all of our editing.
	 * This means we can restore the original screen when we finish. */
	g_hConOut = CreateConsoleScreenBuffer(GENERIC_WRITE | GENERIC_READ,
					      0, (LPSECURITY_ATTRIBUTES) NULL,
					      CONSOLE_TEXTMODE_BUFFER,
					      (LPVOID) NULL);
    }

    coord.X = coord.Y = 0;
    FillConsoleOutputCharacter(g_hConOut, ' ', Rows * Columns, coord, &dwDummy);
    FillConsoleOutputAttribute(g_hConOut, wAttr, Rows*Columns, coord, &dwDummy);

    copy_screen_buffer_text(g_hSavOut, g_hConOut);

    g_hCurOut = g_hConOut;
    SetConsoleActiveScreenBuffer(g_hCurOut);

    ResizeConBufAndWindow(g_hCurOut, Columns, Rows);
    set_scroll_region(0, 0, Columns - 1, Rows - 1);
    check_winsize();

    resettitle();

    textattr(wAttr);
}


/*
 * Turn off termcap mode by restoring the screen buffer we had upon startup
 */
    static void
termcap_mode_end()
{
    g_attrSave = g_attrCurrent;

    ResizeConBufAndWindow(g_hCurOut, g_nOldColumns, g_nOldRows);
    check_winsize();

    g_hCurOut = g_hSavOut;
    SetConsoleActiveScreenBuffer(g_hCurOut);
    SetConsoleCursorInfo(g_hCurOut, &g_cci);

    normvideo();

    if (!p_rs)
	g_coordOrig.Y = g_srOrigWindowRect.Bottom;

    SetConsoleCursorPosition(g_hCurOut, g_coordOrig);

    if (!p_rs)
	copy_screen_buffer_text(g_hConOut, g_hSavOut);

    clear_chars(g_coordOrig,
		g_srOrigWindowRect.Right - g_srOrigWindowRect.Left + 1);

    mch_restore_title(3);

    SetConsoleMode(g_hConIn,  g_cmodein);
    SetConsoleMode(g_hSavOut, g_cmodeout);
}

/*
 * Switching off termcap mode is only allowed when Columns is 80, otherwise a
 * crash may result.  It's always allowed on NT or when running the GUI.
 */
    int
can_end_termcap_mode(
    int give_msg)
{
#ifdef USE_GUI
    if (gui.in_use)	/* GUI starts a new console anyway */
	return TRUE;
#endif
    if (g_PlatformId == VER_PLATFORM_WIN32_NT  ||  Columns == 80)
	return TRUE;
    if (give_msg)
	msg("'columns' is not 80, cannot execute external commands");
    return FALSE;
}

/*
 * clear `n' chars, starting from `coord'
 */
    static void
clear_chars(
    COORD coord,
    DWORD n)
{
    DWORD dwDummy;

    FillConsoleOutputCharacter(g_hCurOut, ' ', n, coord, &dwDummy);
    FillConsoleOutputAttribute(g_hCurOut, g_attrCurrent, n, coord, &dwDummy);
}


/*
 * Clear the screen
 */
    static void
clear_screen(void)
{
    g_coord.X = g_coord.Y = 0;
    clear_chars(g_coord, Rows * Columns);
}


/*
 * Clear to end of display
 */
    static void
clear_to_end_of_display(void)
{
    clear_chars(g_coord, (Rows-g_coord.Y-1) * Columns + (Columns-g_coord.X));
}


/*
 * Clear to end of line
 */
    static void
clear_to_end_of_line(void)
{
    clear_chars(g_coord, Columns - g_coord.X);
}


/*
 * Scroll the scroll region up by `cLines' lines
 */
    static void
scroll(
    unsigned cLines)
{
    COORD oldcoord = g_coord;

    gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
    delete_lines(cLines);

    g_coord = oldcoord;
}


/*
 * Set the scroll region
 */
    static void
set_scroll_region(
    unsigned left,
    unsigned top,
    unsigned right,
    unsigned bottom)
{
    if (left >= right  ||  top >= bottom
	||  right > (unsigned) Columns - 1
	||  bottom > (unsigned) Rows - 1)
	return;

    g_srScrollRegion.Left =   left;
    g_srScrollRegion.Top =    top;
    g_srScrollRegion.Right =  right;
    g_srScrollRegion.Bottom = bottom;
}


/*
 * Insert `cLines' lines at the current cursor position
 */
    static void
insert_lines(
    unsigned cLines)
{
    SMALL_RECT	    source;
    COORD	    dest;
    CHAR_INFO	    fill;

    dest.X = 0;
    dest.Y = g_coord.Y + cLines;

    source.Left   = 0;
    source.Top	  = g_coord.Y;
    source.Right  = g_srScrollRegion.Right;
    source.Bottom = g_srScrollRegion.Bottom - cLines;

    fill.Char.AsciiChar = ' ';
    fill.Attributes = g_attrCurrent;

    ScrollConsoleScreenBuffer(g_hCurOut, &source, NULL, dest, &fill);

    /* Here we have to deal with a win32 console flake: If the scroll
     * region looks like abc and we scroll c to a and fill with d we get
     * cbd... if we scroll block c one line at a time to a, we get cdd...
     * vim expects cdd consistently... So we have to deal with that
     * here... (this also occurs scrolling the same way in the other
     * direction).  */

    if (source.Bottom < dest.Y)
    {
	COORD coord;

	coord.X = 0;
	coord.Y = source.Bottom;
	clear_chars(coord, Columns * (dest.Y - source.Bottom));
    }
}


/*
 * Delete `cLines' lines at the current cursor position
 */
    static void
delete_lines(
    unsigned cLines)
{
    SMALL_RECT	    source;
    COORD	    dest;
    CHAR_INFO	    fill;
    int		    nb;

    dest.X = 0;
    dest.Y = g_coord.Y;

    source.Left   = 0;
    source.Top	  = g_coord.Y + cLines;
    source.Right  = g_srScrollRegion.Right;
    source.Bottom = g_srScrollRegion.Bottom;

    fill.Char.AsciiChar = ' ';
    fill.Attributes = g_attrCurrent;

    ScrollConsoleScreenBuffer(g_hCurOut, &source, NULL, dest, &fill);

    /* Here we have to deal with a win32 console flake: If the scroll
     * region looks like abc and we scroll c to a and fill with d we get
     * cbd... if we scroll block c one line at a time to a, we get cdd...
     * vim expects cdd consistently... So we have to deal with that
     * here... (this also occurs scrolling the same way in the other
     * direction).  */

    nb = dest.Y + (source.Bottom - source.Top) + 1;

    if (nb < source.Top)
    {
	COORD coord;

	coord.X = 0;
	coord.Y = nb;
	clear_chars(coord, Columns * (source.Top - nb));
    }
}


/*
 * Set the cursor position
 */
    static void
gotoxy(
    unsigned x,
    unsigned y)
{
    COORD coord2;

    if (x < 1  ||  x > (unsigned) Columns  ||  y < 1  ||  y > (unsigned) Rows)
	return;

    /* Should we check against g_srScrollRegion? */

    /* external cursor coords are 1-based; internal are 0-based */
    g_coord.X = coord2.X = x - 1;
    g_coord.Y = coord2.Y = y - 1;

    /* If we are using the window buffer that we had upon startup, make
     * sure to position cursor relative to the window upon that buffer */
    if (g_hCurOut == g_hSavOut)
    {
	CONSOLE_SCREEN_BUFFER_INFO csbi;

	GetConsoleScreenBufferInfo(g_hCurOut, &csbi);
	g_srOrigWindowRect = csbi.srWindow;

	coord2.X += (SHORT) (g_srOrigWindowRect.Left);
	coord2.Y += (SHORT) (g_srOrigWindowRect.Bottom + 1 - Rows);
    }

    SetConsoleCursorPosition(g_hCurOut, coord2);
}


/*
 * Set the current text attribute = (foreground | background)
 * See ../doc/os_win32.txt for the numbers.
 */
    static void
textattr(
    WORD wAttr)
{
    g_attrCurrent = wAttr;

    SetConsoleTextAttribute(g_hCurOut, wAttr);
}


    static void
textcolor(
    WORD wAttr)
{
    g_attrCurrent = (g_attrCurrent & 0xf0) + wAttr;

    SetConsoleTextAttribute(g_hCurOut, g_attrCurrent);
}


    static void
textbackground(
    WORD wAttr)
{
    g_attrCurrent = (g_attrCurrent & 0x0f) + (wAttr << 4);

    SetConsoleTextAttribute(g_hCurOut, g_attrCurrent);
}


/*
 * restore the default text attribute (whatever we started with)
 */
    static void
normvideo()
{
    textattr(g_attrDefault);
}


static WORD g_attrPreStandout = 0;

/*
 * Make the text standout, by brightening it
 */
    static void
standout(void)
{
    g_attrPreStandout = g_attrCurrent;
    textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
}


/*
 * Turn off standout mode
 */
    static void
standend()
{
    if (g_attrPreStandout)
    {
	textattr(g_attrPreStandout);
	g_attrPreStandout = 0;
    }
}


/*
 * visual bell: flash the screen
 */
    static void
visual_bell()
{
    COORD   coordOrigin = {0, 0};
    WORD    attrFlash = ~g_attrCurrent & 0xff;

    DWORD   dwDummy;
    LPWORD  oldattrs = (LPWORD) alloc(Rows * Columns * sizeof(WORD));

    ReadConsoleOutputAttribute(g_hCurOut, oldattrs, Rows * Columns,
			       coordOrigin, &dwDummy);
    FillConsoleOutputAttribute(g_hCurOut, attrFlash, Rows * Columns,
			       coordOrigin, &dwDummy);

    Sleep(15);	    /* wait for 15 msec */
    WriteConsoleOutputAttribute(g_hCurOut, oldattrs, Rows * Columns,
				coordOrigin, &dwDummy);
    vim_free(oldattrs);
}


/*
 * Make the cursor visible or invisible
 */
    static void
cursor_visible(
    BOOL fVisible)
{
    CONSOLE_CURSOR_INFO cci;

    cci.bVisible = fVisible;
    /* make cursor big and visible (100 on Win95 makes it disappear)  */
    cci.dwSize = 99;	       /* 100 percent cursor */
    SetConsoleCursorInfo(g_hCurOut, &cci);
}


/*
 * write `cchToWrite' characters in `pchBuf' to the screen
 */
    static BOOL
write_chars(
    LPCSTR pchBuf,
    DWORD  cchToWrite,
    DWORD* pcchWritten)
{
    BOOL f;
    COORD coord2 = g_coord;

    if (g_hCurOut == g_hSavOut)
    {
	CONSOLE_SCREEN_BUFFER_INFO csbi;

	GetConsoleScreenBufferInfo(g_hCurOut, &csbi);

	coord2.X += (SHORT) (csbi.srWindow.Left);
	coord2.Y += (SHORT) (csbi.srWindow.Bottom + 1 - Rows);
    }

    FillConsoleOutputAttribute(g_hCurOut, g_attrCurrent, cchToWrite,
			       coord2, pcchWritten);
    f = WriteConsoleOutputCharacter(g_hCurOut, pchBuf, cchToWrite,
				    coord2, pcchWritten);

    g_coord.X += (SHORT) *pcchWritten;

    while (g_coord.X > g_srScrollRegion.Right)
    {
	g_coord.X -= (SHORT) Columns;
	if (g_coord.Y < g_srScrollRegion.Bottom)
	    g_coord.Y++;
    }

    gotoxy(g_coord.X + 1, g_coord.Y + 1);

    return f;
}


/*
 * mch_write(): write the output buffer to the screen, translating ESC
 * sequences into calls to console output routines.
 */
    void
mch_write(
    char_u *s,
    int len)
{
    s[len] = NUL;

    if (! term_console)
    {
	write(1, s, (unsigned) len);
	return;
    }

    /* translate ESC | sequences into faked bios calls */
    while (len--)
    {
	/* optimization: use one single write_chars for runs of text,
	 * rather than once per character  It ain't curses, but it helps. */

	DWORD  prefix = strcspn(s, "\n\r\b\a\033");

	if (p_wd)
	{
	    WaitForChar(p_wd);
	    if (prefix)
		prefix = 1;
	}

	if (prefix)
	{
	    DWORD nWritten;

	    if (write_chars(s, prefix, &nWritten))
	    {
#ifdef MCH_WRITE_DUMP
		if (fdDump)
		{
		    fputc('>', fdDump);
		    fwrite(s, sizeof(char_u), nWritten, fdDump);
		    fputs("<\n", fdDump);
		}
#endif
		len -= (nWritten - 1);
		s += nWritten;
	    }
	}
	else if (s[0] == '\n')
	{
	    /* \n, newline: go to the beginning of the next line or scroll */
	    if (g_coord.Y == g_srScrollRegion.Bottom)
	    {
		scroll(1);
		gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
	    }
	    else
	    {
		gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
	    }
#ifdef MCH_WRITE_DUMP
	    if (fdDump)
		fputs("\\n\n", fdDump);
#endif
	    s++;
	}
	else if (s[0] == '\r')
	{
	    /* \r, carriage return: go to beginning of line */
	    gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
#ifdef MCH_WRITE_DUMP
	    if (fdDump)
		fputs("\\r\n", fdDump);
#endif
	    s++;
	}
	else if (s[0] == '\b')
	{
	    /* \b, backspace: move cursor one position left */
	    if (g_coord.X > g_srScrollRegion.Left)
		g_coord.X--;
	    else if (g_coord.Y > g_srScrollRegion.Top)
	    {
		g_coord.X = g_srScrollRegion.Right;
		g_coord.Y--;
	    }
	    gotoxy(g_coord.X + 1, g_coord.Y + 1);
#ifdef MCH_WRITE_DUMP
	    if (fdDump)
		fputs("\\b\n", fdDump);
#endif
	    s++;
	}
	else if (s[0] == '\a')
	{
	    /* \a, bell */
	    MessageBeep(0xFFFFFFFF);
#ifdef MCH_WRITE_DUMP
	    if (fdDump)
		fputs("\\a\n", fdDump);
#endif
	    s++;
	}
	else if (s[0] == ESC  &&  len >= 3-1  &&  s[1] == '|')
	{
#ifdef MCH_WRITE_DUMP
	    char_u* old_s = s;
#endif
	    char_u* p;
	    int arg1 = 0, arg2 = 0;

	    switch (s[2])
	    {
	    /* one or two numeric arguments, separated by ';' */

	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7': case '8': case '9':
		p = s + 2;
		arg1 = getdigits(&p);	    /* no check for length! */
		if (p > s + len)
		    break;

		if (*p == ';')
		{
		    ++p;
		    arg2 = getdigits(&p);   /* no check for length! */
		    if (p > s + len)
			break;

		    if (*p == 'H')
			gotoxy(arg2, arg1);
		    else if (*p == 'r')
			set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
		}
		else if (*p == 'A')
		{
		    /* move cursor up arg1 lines in same column */
		    gotoxy(g_coord.X + 1,
			   max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
		}
		else if (*p == 'C')
		{
		    /* move cursor right arg1 columns in same line */
		    gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
			   g_coord.Y + 1);
		}
		else if (*p == 'H')
		{
		    gotoxy(1, arg1);
		}
		else if (*p == 'L')
		{
		    insert_lines(arg1);
		}
		else if (*p == 'm')
		{
		    if (arg1 == 0)
			normvideo();
		    else
			textattr((WORD) arg1);
		}
		else if (*p == 'f')
		{
		    textcolor((WORD) arg1);
		}
		else if (*p == 'b')
		{
		    textbackground((WORD) arg1);
		}
		else if (*p == 'M')
		{
		    delete_lines(arg1);
		}

		len -= p - s;
		s = p + 1;
		break;


	    /* Three-character escape sequences */

	    case 'A':
		/* move cursor up one line in same column */
		gotoxy(g_coord.X + 1,
		       max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
		goto got3;

	    case 'B':
		visual_bell();
		goto got3;

	    case 'C':
		/* move cursor right one column in same line */
		gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
		       g_coord.Y + 1);
		goto got3;

	    case 'E':
		termcap_mode_end();
		goto got3;

	    case 'F':
		standout();
		goto got3;

	    case 'f':
		standend();
		goto got3;

	    case 'H':
		gotoxy(1, 1);
		goto got3;

	    case 'j':
		clear_to_end_of_display();
		goto got3;

	    case 'J':
		clear_screen();
		goto got3;

	    case 'K':
		clear_to_end_of_line();
		goto got3;

	    case 'L':
		insert_lines(1);
		goto got3;

	    case 'M':
		delete_lines(1);
		goto got3;

	    case 'S':
		termcap_mode_start();
		goto got3;

	    case 'V':
		cursor_visible(TRUE);
		goto got3;

	    case 'v':
		cursor_visible(FALSE);
		goto got3;

	    got3:
		s += 3;
		len -= 2;
	    }

#ifdef MCH_WRITE_DUMP
	    if (fdDump)
	    {
		fputs("ESC | ", fdDump);
		fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
		fputc('\n', fdDump);
	    }
#endif
	}
	else
	{
	    /* Write a single character */
	    DWORD nWritten;

	    if (write_chars(s, 1, &nWritten))
	    {
#ifdef MCH_WRITE_DUMP
		if (fdDump)
		{
		    fputc('>', fdDump);
		    fwrite(s, sizeof(char_u), nWritten, fdDump);
		    fputs("<\n", fdDump);
		}
#endif

		len -= (nWritten - 1);
		s += nWritten;
	    }
	}
    }

#ifdef MCH_WRITE_DUMP
    if (fdDump)
	fflush(fdDump);
#endif
}


/*
 * Delay for half a second.
 */
    void
mch_delay(
    long    msec,
    int	    ignoreinput)
{
    if (ignoreinput)
	Sleep((int)msec);
    else
	WaitForChar(msec);
}


#ifdef vim_remove
# undef vim_remove
#endif

/*
 * this version of remove is not scared by a readonly (backup) file
 */
    int
vim_remove(
    char_u *name)
{
    SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
    return DeleteFile(name) ? 0 : -1;
}


/*
 * check for an "interrupt signal": CTRL-break or CTRL-C
 */
    void
mch_breakcheck()
{
    if (g_fCtrlCPressed || g_fCBrkPressed)
    {
	g_fCtrlCPressed = g_fCBrkPressed = FALSE;
	got_int = TRUE;
    }
}


/*
 * How much memory is available?
 */
    long_u
mch_avail_mem(
    int special)
{
    return LONG_MAX;	    /* virtual memory, eh? */
}


/*
 * return non-zero if a character is available
 */
    int
mch_char_avail()
{
    return WaitForChar(0L);
}


/*
 * set screen mode, always fails.
 */
    int
mch_screenmode(
    char_u *arg)
{
    EMSG("Screen mode setting not supported");
    return FAIL;
}


/*
 * win95rename works around a bug in rename (aka MoveFile) in
 * Windows 95: rename("foo.bar", "foo.bar~") will generate a
 * file whose short file name is "FOO.BAR" (its long file name will
 * be correct: "foo.bar~").  Because a file can be accessed by
 * either its SFN or its LFN, "foo.bar" has effectively been
 * renamed to "foo.bar", which is not at all what was wanted.  This
 * seems to happen only when renaming files with three-character
 * extensions by appending a suffix that does not include ".".
 * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
 *
 * Like rename(), returns 0 upon success, non-zero upon failure.
 * Should probably set errno appropriately when errors occur.
 */
    int
win95rename(
    const char* pszOldFile,
    const char* pszNewFile)
{
    char szTempFile[_MAX_PATH];
    char szNewPath[_MAX_PATH];
    char* pszFilePart;
    HANDLE hf;

#undef rename

    /* get base path of new file name */
    if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0)
	return -1;
    else
	*pszFilePart = NUL;

    /* Get (and create) a unique temporary file name in directory of new file */
    if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
	return -2;

    /* blow the temp file away */
    if (! DeleteFile(szTempFile))
	return -3;

    /* rename old file to the temp file */
    if (! MoveFile(pszOldFile, szTempFile))
	return -4;

    /* now create an empty file called pszOldFile; this prevents
     * the operating system using pszOldFile as an alias (SFN)
     * if we're renaming within the same directory.  For example,
     * we're editing a file called filename.asc.txt by its SFN,
     * filena~1.txt.  If we rename filena~1.txt to filena~1.txt~
     * (i.e., we're making a backup while writing it), the SFN
     * for filena~1.txt~ will be filena~1.txt, by default, which
     * will cause all sorts of problems later in buf_write.  So, we
     * create an empty file called filena~1.txt and the system will have
     * to find some other SFN for filena~1.txt~, such as filena~2.txt
     */
    if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
			 FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
	return -5;
    if (! CloseHandle(hf))
	return -6;

    /* rename the temp file to the new file */
    if (! MoveFile(szTempFile, pszNewFile))
	return -7;

    /* Seems to be left around on Novell filesystems */
    DeleteFile(szTempFile);

    /* finally, remove the empty old file */
    if (! DeleteFile(pszOldFile))
	return -8;

    return 0;	/* success */
}

/*
 * Special version of getenv(): use $HOME when $VIM not defined.
 */
    char_u *
vim_getenv(
    char_u *var)
{
    char_u  *retval;
#if 0
    static char *exepath = NULL;
    char temp [ 256 ];
    char *p;
#endif

    retval = (char_u *)getenv((char *)var);

    if (retval == NULL && STRCMP(var, "VIM") == 0)
	retval = (char_u *)getenv("HOME");
#if 0	    /* this is already done in expand_env(), using exe_name. */
// ron's tweak:
    if (retval == NULL && STRCMP(var, "VIM") == 0)
    {
	// get exe name
	if (exepath == NULL)
	{
	    GetModuleFileName(NULL, temp, 255);
	    // find last \\ and truncate there:
	    p = strrchr(temp, '\\');
	    if (p != NULL)
	    {
		*p = '\0';
	    }
	    exepath = strdup(temp);
	}
	retval = (char_u *) exepath;
    }
#endif

#ifdef MCH_WRITE_DUMP
    if (fdDump)
    {
	fprintf(fdDump, "$%s = \"%s\"\n", var, retval);
	fflush(fdDump);
    }
#endif

    return retval;
}


/*
 * Get the default shell for the current hardware platform
 */
    char*
default_shell()
{
    char* psz = NULL;

    PlatformId();

    if (g_PlatformId == VER_PLATFORM_WIN32_NT)		/* Windows NT */
	psz = "cmd.exe";
    else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)  /* Windows 95 */
	psz = "command.com";

    return psz;
}


#ifdef USE_CLIPBOARD
/*
 * Clipboard stuff, for cutting and pasting text to other windows.
 */

/*
 * Get the current selection and put it in the clipboard register.
 */
    void
clip_mch_request_selection()
{
    int	    type;
    HGLOBAL hMem;
    char_u  *str = NULL;

    if (OpenClipboard(GetActiveWindow()))
    {
	/* Check for vim's own clipboard format first */
	if ((hMem = GetClipboardData(clipboard.format)) != NULL)
	{
	    str = (char_u *)hMem;
	    switch (*str++)
	    {
		default:
		case 'L':	type = MLINE;	break;
		case 'C':	type = MCHAR;	break;
		case 'B':	type = MBLOCK;	break;
	    }
	    /* TRACE("Got '%c' type\n", str[-1]); */
	}
	/* Otherwise, check for the normal text format */
	else if ((hMem = GetClipboardData(CF_TEXT)) != NULL)
	{
	    str = (char_u *)hMem;
	    type = (strchr((char*) str, '\r') != NULL) ? MLINE : MCHAR;
	    /* TRACE("TEXT\n"); */
	}

	if (hMem != NULL && *str != NUL)
	{
	    LPCSTR psz = (LPCSTR) str;
	    char_u *temp_clipboard = (char_u *)lalloc(STRLEN(psz) + 1, TRUE);
	    char_u *pszTemp = temp_clipboard;

	    if (temp_clipboard != NULL)
	    {
		while (*psz != NUL)
		{
		    const char* pszNL = strchr(psz, '\r');
		    const int len = (pszNL != NULL) ? pszNL - psz : STRLEN(psz);

		    STRNCPY(pszTemp, psz, len);

		    pszTemp += len;
		    if (pszNL != NULL)
			*pszTemp++ = '\n';

		    psz += len + ((pszNL != NULL) ? 2 : 0);
		}

		*pszTemp = NUL;
		clip_yank_selection(type, temp_clipboard,
				    pszTemp - temp_clipboard);
		vim_free(temp_clipboard);
	    }
	}
	CloseClipboard();
    }
}

/*
 * Make vim the owner of the current selection.
 */
    void
clip_mch_lose_selection()
{
    /* Nothing needs to be done here */
}

/*
 * Send the current selection to the clipboard.  Must own the selection first.
 */
    static void
clip_w32_set_clipboard(void)
{
    char_u  *str = NULL;
    long_u  cch;
    int	    type;
    HGLOBAL hMem = NULL;
    HGLOBAL hMemVim = NULL;
    LPSTR   lpszMem = NULL;
    LPSTR   lpszMemVim = NULL;

    if (!clipboard.owned)
	return;

    clip_get_selection();

    /*
     * Once we set the clipboard, lose ownership.  If another application sets
     * the clipboard, we don't want to think that we still own it.
     */
    clipboard.owned = FALSE;

    type = clip_convert_selection(&str, &cch);

    if (type < 0)
	return;
    
    if ((hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cch+1)) != NULL
	&&  (lpszMem = (LPSTR) GlobalLock(hMem)) != NULL
	&&  (hMemVim = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, cch+2)) != NULL
	&&  (lpszMemVim = (LPSTR) GlobalLock(hMemVim)) != NULL)
    {
	switch (type)
	{
	    default:
	    case MLINE:	    *lpszMemVim++ = 'L';    break;
	    case MCHAR:	    *lpszMemVim++ = 'C';    break;
	    case MBLOCK:    *lpszMemVim++ = 'B';    break;
	}
	
	STRNCPY(lpszMem, str, cch);
	lpszMem[cch] = NUL;
	
	STRNCPY(lpszMemVim, str, cch);
	lpszMemVim[cch] = NUL;

	GlobalUnlock(hMem);
	GlobalUnlock(hMemVim);

	if (OpenClipboard(GetActiveWindow()))
	{
	    if (EmptyClipboard()
		    && SetClipboardData(clipboard.format, hMemVim) != NULL
		    && SetClipboardData(CF_TEXT, hMem) != NULL)

	    CloseClipboard();
	}
    }

    vim_free(str);
}

/*
 * Make vim the owner of the current selection.  Return OK upon success.
 */
    int
clip_mch_own_selection()
{
    clip_w32_set_clipboard();
    return OK;
}

/*
 * We assume that another application will neither get nor set the clipboard
 * contents while we have the focus.  That way we only have to send data to the
 * clipboard when we lose focus. -- webb.
 */
    void
clip_mch_lose_focus()
{
    /*
     * Do nothing.  Now we don't wait till we lose focus.  We just set the
     * clipboard immediately whenever appropriate. -- webb
     */
    /* clip_w32_set_clipboard(); */
}
#endif /* USE_CLIPBOARD */


/*
 * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
 */
    void
DumpPutS(
    const char* psz)
{
# ifdef MCH_WRITE_DUMP
    if (fdDump)
    {
	fputs(psz, fdDump);
	if (psz[strlen(psz) - 1] != '\n')
	    fputc('\n', fdDump);
	fflush(fdDump);
    }
# endif /* MCH_WRITE_DUMP */
}

#ifdef _DEBUG

void __cdecl
Trace(
    char *pszFormat,
    ...)
{
    CHAR szBuff[2048];
    va_list args;

    va_start(args, pszFormat);
    vsprintf(szBuff, pszFormat, args);
    va_end(args);

    OutputDebugString(szBuff);
}

#endif //_DEBUG
