📄 os_win32.c
字号:
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_GUI_WIN32 */
#endif /* USE_MOUSE */
#ifndef USE_GUI_WIN32 /* this isn't used for the GUI */
/*
* Set the shape of the cursor.
* 'thickness' can be from 1 (thin) to 99 (block)
*/
static void
mch_set_cursor_shape(int thickness)
{
CONSOLE_CURSOR_INFO ConsoleCursorInfo;
ConsoleCursorInfo.dwSize = thickness;
ConsoleCursorInfo.bVisible = s_cursor_visible;
SetConsoleCursorInfo(g_hCurOut, &ConsoleCursorInfo);
}
void
mch_update_cursor(void)
{
int idx;
int thickness;
/*
* How the cursor is drawn depends on the current mode.
*/
idx = get_cursor_idx();
if (cursor_table[idx].shape == SHAPE_BLOCK)
thickness = 99; /* 100 doesn't work on W95 */
else
thickness = cursor_table[idx].percentage;
mch_set_cursor_shape(thickness);
}
/*
* Wait until console input from keyboard or mouse is available,
* or the time is up.
* Return TRUE if something is available FALSE if not.
*/
static int
WaitForChar(long msec)
{
DWORD dwNow, dwEndTime;
INPUT_RECORD ir;
DWORD cRecords;
char_u ch, ch2;
if (msec > 0)
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.
*/
for (;;)
{
if (g_chPending != NUL
#ifdef USE_MOUSE
|| g_nMouseClick != -1
#endif
)
return TRUE;
if (msec > 0)
{
dwNow = GetTickCount();
if (dwNow >= dwEndTime)
break;
if (WaitForSingleObject(g_hConIn, dwEndTime - dwNow)
!= WAIT_OBJECT_0)
continue;
}
cRecords = 0;
PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
if (cRecords > 0)
{
#ifdef MULTI_BYTE_IME
/* Windows IME sends two '\n's with only one 'ENTER'.
first, wVirtualKeyCode == 13. second, wVirtualKeyCode == 0 */
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
{
if (ir.Event.KeyEvent.uChar.UnicodeChar == 0
&& ir.Event.KeyEvent.wVirtualKeyCode == 13)
{
ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
continue;
}
return decode_key_event(&ir.Event.KeyEvent, &ch, &ch2, FALSE);
}
#else
if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown
&& decode_key_event(&ir.Event.KeyEvent, &ch, &ch2, FALSE))
return TRUE;
#endif
ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
#ifdef USE_CLIPBOARD
if (ir.EventType == FOCUS_EVENT)
g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
#endif
else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
set_winsize(Rows, Columns, FALSE);
#ifdef USE_MOUSE
else if (ir.EventType == MOUSE_EVENT
&& decode_mouse_event(&ir.Event.MouseEvent))
return TRUE;
#endif
}
else if (msec == 0)
break;
}
return FALSE;
}
/*
* Create the console input. Used when reading stdin doesn't work.
*/
static void
create_conin(void)
{
g_hConIn = CreateFile("CONIN$", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
(LPSECURITY_ATTRIBUTES) NULL,
OPEN_EXISTING, (DWORD)NULL, (HANDLE)NULL);
did_create_conin = TRUE;
}
/*
* Get a keystroke or a mouse event
*/
static char_u
tgetch(void)
{
char_u ch;
if (g_chPending != NUL)
{
ch = g_chPending;
g_chPending = NUL;
return ch;
}
for ( ; ; )
{
INPUT_RECORD ir;
DWORD cRecords = 0;
if (ReadConsoleInput(g_hConIn, &ir, 1, &cRecords) == 0)
{
if (did_create_conin)
read_error_exit();
create_conin();
continue;
}
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); */
}
#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
}
}
#endif /* !USE_GUI_WIN32 */
/*
* 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)
{
#ifndef USE_GUI_WIN32 /* this isn't used for the GUI */
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))
updatescript(0);
}
/*
* Try to read as many characters as there are, until the buffer is full.
*/
/* 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
while ((len == 0 || WaitForChar(0)) && len < maxlen)
{
#ifdef USE_MOUSE
if (g_nMouseClick != -1 && maxlen - len >= 5)
{
# ifdef MCH_WRITE_DUMP
if (fdDump)
fprintf(fdDump, "{%02x @ %d, %d}",
g_nMouseClick, g_xMouse, g_yMouse);
# endif
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
{
*buf++ = c;
len++;
#ifdef MCH_WRITE_DUMP
if (fdDump)
fputc(c, fdDump);
#endif
}
}
}
#ifdef MCH_WRITE_DUMP
if (fdDump)
{
fputs("]\n", fdDump);
fflush(fdDump);
}
#endif
return len;
#else /* USE_GUI_WIN32 */
return 0;
#endif /* USE_GUI_WIN32 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -