terminal/src/host/input.cpp
Josh Soref 5de9fa9cf3
ci: run spell check in CI, fix remaining issues (#4799)
This commit introduces a github action to check our spelling and fixes
the following misspelled words so that we come up green.

It also renames TfEditSes to TfEditSession, because Ses is not a word.

currently, excerpt, fallthrough, identified, occurred, propagate,
provided, rendered, resetting, separate, succeeded, successfully,
terminal, transferred, adheres, breaks, combining, preceded,
architecture, populated, previous, setter, visible, window, within,
appxmanifest, hyphen, control, offset, powerpoint, suppress, parsing,
prioritized, aforementioned, check in, build, filling, indices, layout,
mapping, trying, scroll, terabyte, vetoes, viewport, whose
2020-03-25 11:02:53 -07:00

346 lines
10 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "input.h"
#include "dbcs.h"
#include "stream.h"
#include "..\terminal\input\terminalInput.hpp"
#include "..\interactivity\inc\ServiceLocator.hpp"
#pragma hdrstop
#define KEY_ENHANCED 0x01000000
using Microsoft::Console::Interactivity::ServiceLocator;
bool IsInProcessedInputMode()
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return (gci.pInputBuffer->InputMode & ENABLE_PROCESSED_INPUT) != 0;
}
bool IsInVirtualTerminalInputMode()
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return WI_IsFlagSet(gci.pInputBuffer->InputMode, ENABLE_VIRTUAL_TERMINAL_INPUT);
}
BOOL IsSystemKey(const WORD wVirtualKeyCode)
{
switch (wVirtualKeyCode)
{
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
case VK_PAUSE:
case VK_CAPITAL:
case VK_LWIN:
case VK_RWIN:
case VK_NUMLOCK:
case VK_SCROLL:
return TRUE;
}
return FALSE;
}
ULONG GetControlKeyState(const LPARAM lParam)
{
ULONG ControlKeyState = 0;
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_LMENU) & KEY_PRESSED)
{
ControlKeyState |= LEFT_ALT_PRESSED;
}
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_RMENU) & KEY_PRESSED)
{
ControlKeyState |= RIGHT_ALT_PRESSED;
}
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_LCONTROL) & KEY_PRESSED)
{
ControlKeyState |= LEFT_CTRL_PRESSED;
}
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_RCONTROL) & KEY_PRESSED)
{
ControlKeyState |= RIGHT_CTRL_PRESSED;
}
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_SHIFT) & KEY_PRESSED)
{
ControlKeyState |= SHIFT_PRESSED;
}
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_NUMLOCK) & KEY_TOGGLED)
{
ControlKeyState |= NUMLOCK_ON;
}
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_SCROLL) & KEY_TOGGLED)
{
ControlKeyState |= SCROLLLOCK_ON;
}
if (ServiceLocator::LocateInputServices()->GetKeyState(VK_CAPITAL) & KEY_TOGGLED)
{
ControlKeyState |= CAPSLOCK_ON;
}
if (lParam & KEY_ENHANCED)
{
ControlKeyState |= ENHANCED_KEY;
}
ControlKeyState |= (lParam & ALTNUMPAD_BIT);
return ControlKeyState;
}
// Routine Description:
// - returns true if we're in a mode amenable to us taking over keyboard shortcuts
bool ShouldTakeOverKeyboardShortcuts()
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return !gci.GetCtrlKeyShortcutsDisabled() && IsInProcessedInputMode();
}
// Routine Description:
// - handles key events without reference to Win32 elements.
void HandleGenericKeyEvent(_In_ KeyEvent keyEvent, const bool generateBreak)
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
bool ContinueProcessing = true;
if (keyEvent.IsCtrlPressed() &&
!keyEvent.IsAltPressed() &&
keyEvent.IsKeyDown())
{
// check for ctrl-c, if in line input mode.
if (keyEvent.GetVirtualKeyCode() == 'C' && IsInProcessedInputMode())
{
HandleCtrlEvent(CTRL_C_EVENT);
if (gci.PopupCount == 0)
{
gci.pInputBuffer->TerminateRead(WaitTerminationReason::CtrlC);
}
if (!(WI_IsFlagSet(gci.Flags, CONSOLE_SUSPENDED)))
{
ContinueProcessing = false;
}
}
// Check for ctrl-break.
else if (keyEvent.GetVirtualKeyCode() == VK_CANCEL)
{
gci.pInputBuffer->Flush();
HandleCtrlEvent(CTRL_BREAK_EVENT);
if (gci.PopupCount == 0)
{
gci.pInputBuffer->TerminateRead(WaitTerminationReason::CtrlBreak);
}
if (!(WI_IsFlagSet(gci.Flags, CONSOLE_SUSPENDED)))
{
ContinueProcessing = false;
}
}
// don't write ctrl-esc to the input buffer
else if (keyEvent.GetVirtualKeyCode() == VK_ESCAPE)
{
ContinueProcessing = false;
}
}
else if (keyEvent.IsAltPressed() &&
keyEvent.IsKeyDown() &&
keyEvent.GetVirtualKeyCode() == VK_ESCAPE)
{
ContinueProcessing = false;
}
if (ContinueProcessing)
{
size_t EventsWritten = 0;
try
{
EventsWritten = gci.pInputBuffer->Write(std::make_unique<KeyEvent>(keyEvent));
if (EventsWritten && generateBreak)
{
keyEvent.SetKeyDown(false);
EventsWritten = gci.pInputBuffer->Write(std::make_unique<KeyEvent>(keyEvent));
}
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}
}
#ifdef DBG
// set to true with a debugger to temporarily disable focus events getting written to the InputBuffer
volatile bool DisableFocusEvents = false;
#endif
void HandleFocusEvent(const BOOL fSetFocus)
{
#ifdef DBG
if (DisableFocusEvents)
{
return;
}
#endif
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
try
{
const size_t EventsWritten = gci.pInputBuffer->Write(std::make_unique<FocusEvent>(!!fSetFocus));
FAIL_FAST_IF(EventsWritten != 1);
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}
void HandleMenuEvent(const DWORD wParam)
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
size_t EventsWritten = 0;
try
{
EventsWritten = gci.pInputBuffer->Write(std::make_unique<MenuEvent>(wParam));
if (EventsWritten != 1)
{
RIPMSG0(RIP_WARNING, "PutInputInBuffer: EventsWritten != 1, 1 expected");
}
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}
void HandleCtrlEvent(const DWORD EventType)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
switch (EventType)
{
case CTRL_C_EVENT:
gci.CtrlFlags |= CONSOLE_CTRL_C_FLAG;
break;
case CTRL_BREAK_EVENT:
gci.CtrlFlags |= CONSOLE_CTRL_BREAK_FLAG;
break;
case CTRL_CLOSE_EVENT:
gci.CtrlFlags |= CONSOLE_CTRL_CLOSE_FLAG;
break;
default:
RIPMSG1(RIP_ERROR, "Invalid EventType: 0x%x", EventType);
}
}
void ProcessCtrlEvents()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (gci.CtrlFlags == 0)
{
gci.UnlockConsole();
return;
}
// Make our own copy of the console process handle list.
DWORD const LimitingProcessId = gci.LimitingProcessId;
gci.LimitingProcessId = 0;
ConsoleProcessTerminationRecord* rgProcessHandleList;
size_t cProcessHandleList;
HRESULT hr = gci.ProcessHandleList
.GetTerminationRecordsByGroupId(LimitingProcessId,
WI_IsFlagSet(gci.CtrlFlags,
CONSOLE_CTRL_CLOSE_FLAG),
&rgProcessHandleList,
&cProcessHandleList);
if (FAILED(hr) || cProcessHandleList == 0)
{
gci.UnlockConsole();
return;
}
// Copy ctrl flags.
ULONG CtrlFlags = gci.CtrlFlags;
FAIL_FAST_IF(!(!((CtrlFlags & (CONSOLE_CTRL_CLOSE_FLAG | CONSOLE_CTRL_BREAK_FLAG | CONSOLE_CTRL_C_FLAG)) && (CtrlFlags & (CONSOLE_CTRL_LOGOFF_FLAG | CONSOLE_CTRL_SHUTDOWN_FLAG)))));
gci.CtrlFlags = 0;
gci.UnlockConsole();
// the ctrl flags could be a combination of the following
// values:
//
// CONSOLE_CTRL_C_FLAG
// CONSOLE_CTRL_BREAK_FLAG
// CONSOLE_CTRL_CLOSE_FLAG
// CONSOLE_CTRL_LOGOFF_FLAG
// CONSOLE_CTRL_SHUTDOWN_FLAG
DWORD EventType = (DWORD)-1;
switch (CtrlFlags & (CONSOLE_CTRL_CLOSE_FLAG | CONSOLE_CTRL_BREAK_FLAG | CONSOLE_CTRL_C_FLAG | CONSOLE_CTRL_LOGOFF_FLAG | CONSOLE_CTRL_SHUTDOWN_FLAG))
{
case CONSOLE_CTRL_CLOSE_FLAG:
EventType = CTRL_CLOSE_EVENT;
break;
case CONSOLE_CTRL_BREAK_FLAG:
EventType = CTRL_BREAK_EVENT;
break;
case CONSOLE_CTRL_C_FLAG:
EventType = CTRL_C_EVENT;
break;
case CONSOLE_CTRL_LOGOFF_FLAG:
EventType = CTRL_LOGOFF_EVENT;
break;
case CONSOLE_CTRL_SHUTDOWN_FLAG:
EventType = CTRL_SHUTDOWN_EVENT;
break;
}
NTSTATUS Status = STATUS_SUCCESS;
for (size_t i = 0; i < cProcessHandleList; i++)
{
/*
* Status will be non-successful if a process attached to this console
* vetoes shutdown. In that case, we don't want to try to kill any more
* processes, but we do need to make sure we continue looping so we
* can close any remaining process handles. The exception is if the
* process is inaccessible, such that we can't even open a handle for
* query. In this case, use best effort to send the close event but
* ignore any errors.
*/
if (NT_SUCCESS(Status))
{
Status = ServiceLocator::LocateConsoleControl()
->EndTask((HANDLE)rgProcessHandleList[i].dwProcessID,
EventType,
CtrlFlags);
if (rgProcessHandleList[i].hProcess == nullptr)
{
Status = STATUS_SUCCESS;
}
}
if (rgProcessHandleList[i].hProcess != nullptr)
{
CloseHandle(rgProcessHandleList[i].hProcess);
}
}
delete[] rgProcessHandleList;
}