can type emoji and run commands

This commit is contained in:
Austin Diviness 2019-04-15 19:09:36 -07:00
parent 6c80ab8017
commit 899dccfec9
13 changed files with 383 additions and 27 deletions

View file

@ -131,6 +131,9 @@ CommandLine& CommandLine::Instance()
bool CommandLine::IsEditLineEmpty() const
{
// TODO
return false;
/*
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (!gci.HasPendingCookedRead())
@ -148,26 +151,34 @@ bool CommandLine::IsEditLineEmpty() const
{
return false;
}
*/
}
void CommandLine::Hide(const bool fUpdateFields)
{
// TODO
fUpdateFields;
/*
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (!IsEditLineEmpty())
{
DeleteCommandLine(gci.CookedReadData(), fUpdateFields);
}
*/
_isVisible = false;
}
void CommandLine::Show()
{
_isVisible = true;
return;
/*
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (!IsEditLineEmpty())
{
RedrawCommandLine(gci.CookedReadData());
}
*/
}
// Routine Description:

View file

@ -62,6 +62,7 @@ Notes:
#include "alias.h"
#include "readDataCooked.hpp"
#include "popup.h"
#include "cookedRead.hpp"
class CommandLine

View file

@ -163,17 +163,17 @@ bool CONSOLE_INFORMATION::HasPendingCookedRead() const noexcept
return _cookedReadData != nullptr;
}
const COOKED_READ_DATA& CONSOLE_INFORMATION::CookedReadData() const noexcept
const CookedRead& CONSOLE_INFORMATION::CookedReadData() const noexcept
{
return *_cookedReadData;
}
COOKED_READ_DATA& CONSOLE_INFORMATION::CookedReadData() noexcept
CookedRead& CONSOLE_INFORMATION::CookedReadData() noexcept
{
return *_cookedReadData;
}
void CONSOLE_INFORMATION::SetCookedReadData(COOKED_READ_DATA* readData) noexcept
void CONSOLE_INFORMATION::SetCookedReadData(CookedRead* readData) noexcept
{
_cookedReadData = readData;
}

268
src/host/cookedRead.cpp Normal file
View file

@ -0,0 +1,268 @@
/********************************************************
* *
* Copyright (C) Microsoft. All rights reserved. *
* *
********************************************************/
#include "precomp.h"
#include "cookedRead.hpp"
#include "stream.h"
#include "_stream.h"
#include "../types/inc/Utf16Parser.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
CookedRead::CookedRead(InputBuffer* const pInputBuffer,
INPUT_READ_HANDLE_DATA* const pInputReadHandleData,
SCREEN_INFORMATION& screenInfo,
CommandHistory* const pCommandHistory,
wchar_t* userBuffer,
const size_t cchUserBuffer,
const ULONG ctrlWakeupMask
) :
ReadData(pInputBuffer, pInputReadHandleData),
_screenInfo{ screenInfo },
_userBuffer{ userBuffer },
_cchUserBuffer{ cchUserBuffer },
_ctrlWakeupMask{ ctrlWakeupMask },
_pCommandHistory{ pCommandHistory },
_insertMode{ ServiceLocator::LocateGlobals().getConsoleInformation().GetInsertMode() }
{
_prompt.reserve(256);
_promptStartLocation = _screenInfo.GetTextBuffer().GetCursor().GetPosition();
}
void CookedRead::Erase()
{
_prompt.erase();
}
void CookedRead::SetInsertMode(const bool mode) noexcept
{
_insertMode = mode;
}
COORD CookedRead::PromptStartLocation() const noexcept
{
return _promptStartLocation;
}
size_t CookedRead::VisibleCharCount() const
{
return std::count_if(_prompt.begin(),
_prompt.end(),
[](const wchar_t& wch){ return !Utf16Parser::IsTrailingSurrogate(wch); });
}
bool CookedRead::_isCtrlWakeupMaskTriggered(const wchar_t wch) const noexcept
{
// can't use wil flag set macros here because they require a static scope for the flag
const ULONG flag = 1 << wch;
const bool masked = (_ctrlWakeupMask & flag) != 0;
return (wch < UNICODE_SPACE && masked);
}
// Routine Description:
// - This routine is called to complete a cooked read that blocked in ReadInputBuffer.
// - The context of the read was saved in the CookedReadData structure.
// - This routine is called when events have been written to the input buffer.
// - It is called in the context of the writing thread.
// - It may be called more than once.
// Arguments:
// - TerminationReason - if this routine is called because a ctrl-c or ctrl-break was seen, this argument
// contains CtrlC or CtrlBreak. If the owning thread is exiting, it will have ThreadDying. Otherwise 0.
// - fIsUnicode - Whether to convert the final data to A (using Console Input CP) at the end or treat everything as Unicode (UCS-2)
// - pReplyStatus - The status code to return to the client application that originally called the API (before it was queued to wait)
// - pNumBytes - The number of bytes of data that the server/driver will need to transmit back to the client process
// - pControlKeyState - For certain types of reads, this specifies which modifier keys were held.
// - pOutputData - not used
// Return Value:
// - true if the wait is done and result buffer/status code can be sent back to the client.
// - false if we need to continue to wait until more data is available.
bool CookedRead::Notify(const WaitTerminationReason TerminationReason,
const bool fIsUnicode,
_Out_ NTSTATUS* const pReplyStatus,
_Out_ size_t* const pNumBytes,
_Out_ DWORD* const pControlKeyState,
_Out_ void* const pOutputData)
{
// TODO
pOutputData;
pNumBytes;
fIsUnicode;
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// this routine should be called by a thread owning the same
// lock on the same console as we're reading from.
FAIL_FAST_IF(!gci.IsConsoleLocked());
*pNumBytes = 0;
*pControlKeyState = 0;
*pReplyStatus = STATUS_SUCCESS;
FAIL_FAST_IF(_pInputReadHandleData->IsInputPending());
// this routine should be called by a thread owning the same lock on the same console as we're reading from.
FAIL_FAST_IF(_pInputReadHandleData->GetReadCount() == 0);
// if ctrl-c or ctrl-break was seen, terminate read.
if (WI_IsAnyFlagSet(TerminationReason, (WaitTerminationReason::CtrlC | WaitTerminationReason::CtrlBreak)))
{
*pReplyStatus = STATUS_ALERTED;
gci.SetCookedReadData(nullptr);
return true;
}
// See if we were called because the thread that owns this wait block is exiting.
if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::ThreadDying))
{
*pReplyStatus = STATUS_THREAD_IS_TERMINATING;
gci.SetCookedReadData(nullptr);
return true;
}
// We must see if we were woken up because the handle is being closed. If
// so, we decrement the read count. If it goes to zero, we wake up the
// close thread. Otherwise, we wake up any other thread waiting for data.
if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::HandleClosing))
{
*pReplyStatus = STATUS_ALERTED;
gci.SetCookedReadData(nullptr);
return true;
}
// TODO insert command history code checking here
*pReplyStatus = Read(fIsUnicode, *pNumBytes, *pControlKeyState);
if (*pReplyStatus != CONSOLE_STATUS_WAIT)
{
gci.SetCookedReadData(nullptr);
return true;
}
else
{
return false;
}
}
[[nodiscard]]
NTSTATUS CookedRead::Read(const bool isUnicode,
size_t& numBytes,
ULONG& controlKeyState) noexcept
{
// TODO
isUnicode;
controlKeyState = 0;
NTSTATUS Status = _readCharInputLoop(isUnicode);
// if the read was completed (status != wait), free the cooked read
// data. also, close the temporary output handle that was opened to
// echo the characters read.
// TODO
/*
if (Status != CONSOLE_STATUS_WAIT)
{
Status = _handlePostCharInputLoop(isUnicode, numBytes, controlKeyState);
}
*/
numBytes = _prompt.size() * sizeof(wchar_t);
return Status;
}
[[nodiscard]]
NTSTATUS CookedRead::_readCharInputLoop(const bool isUnicode) noexcept
{
// TODO
isUnicode;
NTSTATUS Status = STATUS_SUCCESS;
while (true)
{
wchar_t wch = UNICODE_NULL;
Status = GetChar(_pInputBuffer,
&wch,
true,
nullptr,
nullptr,
nullptr);
if (!NT_SUCCESS(Status))
{
if (Status != CONSOLE_STATUS_WAIT)
{
Erase();
}
break;
}
_prompt.push_back(wch);
if (!Utf16Parser::IsLeadingSurrogate(wch))
{
if (_processInput())
{
Status = STATUS_SUCCESS;
break;
}
}
}
return Status;
}
bool CookedRead::_isTailSurrogatePair() const
{
return (_prompt.size() >= 2 &&
Utf16Parser::IsTrailingSurrogate(_prompt.back()) &&
Utf16Parser::IsLeadingSurrogate(*(_prompt.crbegin() + 1)));
}
bool CookedRead::_processInput()
{
if (_prompt.back() == UNICODE_CARRIAGERETURN)
{
_prompt.push_back(UNICODE_LINEFEED);
}
LOG_IF_FAILED(_screenInfo.SetCursorPosition(_promptStartLocation, true));
size_t bytesToWrite = _prompt.size() * sizeof(wchar_t);
short scrollY = 0;
NTSTATUS Status = WriteCharsLegacy(_screenInfo,
_prompt.c_str(),
_prompt.c_str(),
_prompt.c_str(),
&bytesToWrite,
nullptr,
_promptStartLocation.X,
WC_DESTRUCTIVE_BACKSPACE | WC_KEEP_CURSOR_VISIBLE | WC_ECHO,
&scrollY);
Status;
std::copy_n(_prompt.begin(), _prompt.size(), _userBuffer);
if (_prompt.back() == UNICODE_LINEFEED)
{
return true;
}
else
{
return false;
}
}

60
src/host/cookedRead.hpp Normal file
View file

@ -0,0 +1,60 @@
#pragma once
#include "readData.hpp"
#include "screenInfo.hpp"
#include "history.h"
class CookedRead final : public ReadData
{
public:
CookedRead(InputBuffer* const pInputBuffer,
INPUT_READ_HANDLE_DATA* const pInputReadHandleData,
SCREEN_INFORMATION& screenInfo,
CommandHistory* const pCommandHistory,
wchar_t* userBuffer,
const size_t cchUserBuffer,
const ULONG ctrlWakeupMask
);
bool Notify(const WaitTerminationReason TerminationReason,
const bool fIsUnicode,
_Out_ NTSTATUS* const pReplyStatus,
_Out_ size_t* const pNumBytes,
_Out_ DWORD* const pControlKeyState,
_Out_ void* const pOutputData);
[[nodiscard]]
NTSTATUS Read(const bool isUnicode,
size_t& numBytes,
ULONG& controlKeyState) noexcept;
void Erase();
void SetInsertMode(const bool mode) noexcept;
COORD PromptStartLocation() const noexcept;
size_t VisibleCharCount() const;
private:
[[nodiscard]]
NTSTATUS _readCharInputLoop(const bool isUnicode) noexcept;
bool _isTailSurrogatePair() const;
bool _processInput();
bool _isCtrlWakeupMaskTriggered(const wchar_t wch) const noexcept;
bool _insertMode;
wchar_t* _userBuffer;
size_t _cchUserBuffer;
SCREEN_INFORMATION& _screenInfo;
std::wstring _prompt;
COORD _promptStartLocation;
CommandHistory* const _pCommandHistory; // non-ownership pointer
const ULONG _ctrlWakeupMask;
};

View file

@ -19,6 +19,7 @@
#include "ApiRoutines.h"
#include "..\interactivity\inc\ServiceLocator.hpp"
#include "cookedRead.hpp"
#pragma hdrstop
@ -347,6 +348,7 @@ HRESULT ApiRoutines::SetConsoleInputModeImpl(InputBuffer& context, const ULONG m
if (gci.HasPendingCookedRead())
{
gci.CookedReadData().SetInsertMode(gci.GetInsertMode());
;
}
}
}

View file

@ -11,6 +11,7 @@
<ClCompile Include="..\ConsoleArguments.cpp" />
<ClCompile Include="..\CursorBlinker.cpp" />
<ClCompile Include="..\readDataCooked.cpp" />
<ClCompile Include="..\cookedRead.cpp" />
<ClCompile Include="..\conareainfo.cpp" />
<ClCompile Include="..\conimeinfo.cpp" />
<ClCompile Include="..\consoleInformation.cpp" />
@ -96,6 +97,7 @@
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\readData.hpp" />
<ClInclude Include="..\readDataCooked.hpp" />
<ClInclude Include="..\cookedRead.hpp" />
<ClInclude Include="..\rectreadDataDirect.hpp" />
<ClInclude Include="..\readDataRaw.hpp" />
<ClInclude Include="..\registry.hpp" />

View file

@ -4,6 +4,7 @@
#include "precomp.h"
#include "search.h"
#include "cookedRead.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/convert.hpp"
@ -954,8 +955,8 @@ bool Selection::s_GetInputLineBoundaries(_Out_opt_ COORD* const pcoordInputStart
}
const auto& cookedRead = gci.CookedReadData();
const COORD coordStart = cookedRead.OriginalCursorPosition();
COORD coordEnd = cookedRead.OriginalCursorPosition();
const COORD coordStart = cookedRead.PromptStartLocation();
COORD coordEnd = coordStart;
if (coordEnd.X < 0 && coordEnd.Y < 0)
{

View file

@ -69,7 +69,7 @@ Revision History:
#define CONSOLE_SUSPENDED (CONSOLE_OUTPUT_SUSPENDED)
class COOKED_READ_DATA;
class CookedRead;
class CommandHistory;
class CONSOLE_INFORMATION :
@ -125,9 +125,9 @@ public:
bool IsInVtIoMode() const;
bool HasPendingCookedRead() const noexcept;
const COOKED_READ_DATA& CookedReadData() const noexcept;
COOKED_READ_DATA& CookedReadData() noexcept;
void SetCookedReadData(COOKED_READ_DATA* readData) noexcept;
const CookedRead& CookedReadData() const noexcept;
CookedRead& CookedReadData() noexcept;
void SetCookedReadData(CookedRead* readData) noexcept;
COLORREF GetDefaultForeground() const noexcept;
COLORREF GetDefaultBackground() const noexcept;
@ -160,7 +160,7 @@ private:
std::wstring _OriginalTitle;
std::wstring _LinkTitle; // Path to .lnk file
SCREEN_INFORMATION* pCurrentScreenBuffer;
COOKED_READ_DATA* _cookedReadData; // non-ownership pointer
CookedRead* _cookedReadData; // non-ownership pointer
Microsoft::Console::VirtualTerminal::VtIo _vtIo;
Microsoft::Console::CursorBlinker _blinker;

View file

@ -17,6 +17,8 @@
#include "..\interactivity\inc\ServiceLocator.hpp"
#include "cookedRead.hpp"
#pragma hdrstop
// Routine Description:
@ -445,7 +447,7 @@ static NTSTATUS _ReadPendingInput(InputBuffer& inputBuffer,
// - bytesRead - on output, the number of bytes read into pwchBuffer
// - controlKeyState - set by a cooked read
// - initialData - text of initial data found in the read message
// - ctrlWakeupMask - used by COOKED_READ_DATA
// - ctrlWakeupMask - used by COOKED_READ_DATA to trigger an early return of reading if masked key is pressed
// - readHandleState - input read handle data associated with this read operation
// - exeName - name of the exe requesting the read
// - unicode - true if read should be unicode, false otherwise
@ -461,10 +463,10 @@ static HRESULT _ReadLineInput(InputBuffer& inputBuffer,
gsl::span<char> buffer,
size_t& bytesRead,
DWORD& controlKeyState,
const std::string_view initialData,
const std::string_view /*initialData*/,
const DWORD ctrlWakeupMask,
INPUT_READ_HANDLE_DATA& readHandleState,
const std::wstring_view exeName,
const std::wstring_view /*exeName*/,
const bool unicode,
std::unique_ptr<IWaitRoutine>& waiter) noexcept
{
@ -476,18 +478,17 @@ static HRESULT _ReadLineInput(InputBuffer& inputBuffer,
try
{
auto cookedReadData = std::make_unique<COOKED_READ_DATA>(&inputBuffer, // pInputBuffer
&readHandleState, // pInputReadHandleData
screenInfo, // pScreenInfo
buffer.size_bytes(), // UserBufferSize
reinterpret_cast<wchar_t*>(buffer.data()), // UserBuffer
ctrlWakeupMask, // CtrlWakeupMask
pCommandHistory, // CommandHistory
exeName, // exe name
initialData);
// TODO hook up other arguments to CookedRead
auto cookedReadData = std::make_unique<CookedRead>(&inputBuffer,
&readHandleState,
screenInfo,
pCommandHistory,
reinterpret_cast<wchar_t*>(buffer.data()),
buffer.size_bytes() / sizeof(wchar_t),
ctrlWakeupMask);
gci.SetCookedReadData(cookedReadData.get());
bytesRead = buffer.size_bytes(); // This parameter on the way in is the size to read, on the way out, it will be updated to what is actually read.
// bytesRead on the way in is the size to read, on the way out, it will be updated to what is actually read.
bytesRead = buffer.size_bytes();
if (CONSOLE_STATUS_WAIT == cookedReadData->Read(unicode, bytesRead, controlKeyState))
{
// memory will be cleaned up by wait queue
@ -497,6 +498,7 @@ static HRESULT _ReadLineInput(InputBuffer& inputBuffer,
{
gci.SetCookedReadData(nullptr);
}
}
CATCH_RETURN();

View file

@ -7,19 +7,23 @@
<ClCompile Include="AttrRowTests.cpp" />
<ClCompile Include="ClipboardTests.cpp" />
<ClCompile Include="ConsoleArgumentsTests.cpp" />
<ClCompile Include="CommandLineTests.cpp" />
<ClCompile Include="CodepointWidthDetectorTests.cpp" />
<!-- TODO
<ClCompile Include="CommandLineTests.cpp" />
<ClCompile Include="CommandListPopupTests.cpp" />
<ClCompile Include="CommandNumberPopupTests.cpp" />
<ClCompile Include="CopyFromCharPopupTests.cpp" />
<ClCompile Include="CopyToCharPopupTests.cpp" />
<ClCompile Include="SelectionTests.cpp" />
-->
<ClCompile Include="DbcsTests.cpp" />
<ClCompile Include="HistoryTests.cpp" />
<ClCompile Include="InitTests.cpp" />
<ClCompile Include="OutputCellIteratorTests.cpp" />
<ClCompile Include="ScreenBufferTests.cpp" />
<ClCompile Include="SearchTests.cpp" />
<ClCompile Include="SelectionTests.cpp" />
<ClCompile Include="TextBufferIteratorTests.cpp" />
<ClCompile Include="TextBufferTests.cpp" />
<ClCompile Include="TitleTests.cpp" />
@ -105,4 +109,4 @@
<Import Project="$(SolutionDir)src\common.build.dll.props" />
<Import Project="$(SolutionDir)src\common.build.post.props" />
<Import Project="$(SolutionDir)src\common.build.tests.props" />
</Project>
</Project>

View file

@ -27,6 +27,7 @@ unit testing projects in the codebase without a bunch of overhead.
#include "../host/inputReadHandleData.h"
#include "../buffer/out/CharRow.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../host/cookedRead.hpp"
class CommonState
{
@ -124,6 +125,8 @@ public:
void PrepareCookedReadData()
{
// TODO
/*
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto* readData = new COOKED_READ_DATA(gci.pInputBuffer,
m_readHandle.get(),
@ -135,6 +138,7 @@ public:
L"",
{});
gci.SetCookedReadData(readData);
*/
}
void CleanupCookedReadData()

View file

@ -15,6 +15,7 @@
#include "..\..\host\server.h"
#include "..\..\host\scrolling.hpp"
#include "..\..\host\telemetry.hpp"
#include "..\..\host\cookedRead.hpp"
#include "..\inc\ServiceLocator.hpp"