📄 os_win32.c
字号:
/* vi:set ts=8 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
*
* Used for both the console version and the Win32 GUI. A lot of code is for
* the console version only, so there is a lot of "#ifndef USE_GUI_WIN32".
*
* 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 <georgere@microsoft.com> wrote most of this.
* Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
*/
#include <io.h>
#include "vim.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
#ifdef __GNUC__
#include <dirent.h>
#else
#include <direct.h>
#endif
/* Record all output and all keyboard & mouse input */
/* #define MCH_WRITE_DUMP */
#ifdef MCH_WRITE_DUMP
FILE* fdDump = NULL;
#endif
/*
* 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
#ifndef USE_GUI_WIN32
/* 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); */
#if defined(__BORLANDC__) || defined(__GNUC__)
typedef BOOL (*PFNGCKLN)(LPSTR);
#else
typedef WINBASEAPI BOOL (WINAPI *PFNGCKLN)(LPSTR);
#endif
PFNGCKLN s_pfnGetConsoleKeyboardLayoutName = NULL;
#endif
#if defined(__GNUC__) && !defined(PROTO)
int _stricoll(char *a, char *b)
{
// 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);
}
char * _fullpath(char *buf, char *fname, int len)
{
LPTSTR toss;
return (char *) GetFullPathName(fname, len, buf, &toss);
}
int _chdrive(int drive)
{
char temp [3] = "-:";
temp[0] = drive + 'A' - 1;
return !SetCurrentDirectory(temp);
}
#else
#ifdef __BORLANDC__
// being a more ANSI compliant compiler, BorlandC doesn't define _stricoll:
// but it does in BC 5.02!
#if __BORLANDC__ < 0x502
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
#endif
#endif
#ifndef USE_GUI_WIN32
/* 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 char_u g_chPending = NUL;
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 void create_conin(void);
static void mch_set_cursor_shape(int thickness);
static int s_cursor_visible = TRUE;
static int did_create_conin = FALSE;
#else
static int s_dont_use_vimrun = TRUE;
static char *vimrun_path = "vimrun ";
#endif
/*
* The old solution for opening a console is still there.
*/
//#define OLD_CONSOLE_STUFF
#ifdef OLD_CONSOLE_STUFF
static void mch_open_console(void);
static void mch_close_console(int wait_key, DWORD ret);
#endif
struct growarray error_ga = {0, 0, 0, 0, NULL};
#ifndef USE_GUI_WIN32
static int suppress_winsize = 1; /* don't fiddle with console */
#endif
/* 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 void PlatformId(void);
static DWORD g_PlatformId;
/*
* Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or
* VER_PLATFORM_WIN32_WINDOWS (Win95).
*/
static void
PlatformId(void)
{
static int done = FALSE;
if (!done)
{
OSVERSIONINFO ovi;
ovi.dwOSVersionInfoSize = sizeof(ovi);
GetVersionEx(&ovi);
g_PlatformId = ovi.dwPlatformId;
done = TRUE;
}
}
/*
* Return TRUE when running on Windows 95. Only to be used after
* mch_windinit().
*/
int
mch_windows95(void)
{
return g_PlatformId == VER_PLATFORM_WIN32_WINDOWS;
}
#ifdef USE_GUI_WIN32
/*
* Used to work around the "can't do synchronous spawn"
* problem on Win32s, without resorting to Universal Thunk.
*/
static int old_num_windows;
static int num_windows;
static BOOL CALLBACK
win32ssynch_cb(HWND hwnd, LPARAM lparam)
{
num_windows++;
return TRUE;
}
#endif
#ifndef USE_GUI_WIN32
#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)
/* 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)
{
#ifdef __GNUC__
pker->AsciiChar = (CHAR) awAnsiCode[1];
#else
pker->uChar.AsciiChar = (CHAR) awAnsiCode[1];
#endif
s_iIsDead = 0;
return 1;
}
#ifdef __GNUC__
if (pker->AsciiChar != 0)
#else
if (pker->uChar.AsciiChar != 0)
#endif
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))
(void)LoadKeyboardLayout(szKLID, KLF_ACTIVATE);
}
/* 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)
#ifdef __GNUC__
pker->AsciiChar = (CHAR) awAnsiCode[0];
#else
pker->uChar.AsciiChar = (CHAR) awAnsiCode[0];
#endif
return s_iIsDead;
}
/* MUST switch optimization on again here, otherwise a call to
* decode_key_event() may crash (e.g. when hitting caps-lock) */
#pragma optimize("", on)
#if (_MSC_VER < 1100)
/* MUST turn off global optimisation for this next function, or
* pressing ctrl-minus in insert mode crashes Vim when built with
* VC4.1. -- negri. */
#pragma optimize("g", off)
#endif
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);
*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;
}
/* special cases */
if ((nModifs & CTRL) != 0 && (nModifs & ~CTRL) == 0
#ifdef __GNUC__
&& pker->AsciiChar == NUL)
#else
&& pker->uChar.AsciiChar == NUL)
#endif
{
/* 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;
else
#ifdef __GNUC__
*pch = (i > 0) ? pker->AsciiChar : NUL;
#else
*pch = (i > 0) ? pker->uChar.AsciiChar : NUL;
#endif
return (*pch != NUL);
}
#pragma optimize("", on)
#endif /* USE_GUI_WIN32 */
#ifdef USE_MOUSE
/*
* For the GUI the mouse handling is in gui_w32.c.
*/
# ifdef USE_GUI_WIN32
void
mch_setmouse(
int on)
{
}
# else
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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -