mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-01 01:00:15 -04:00
Initial release of the Windows Terminal source code
This commit introduces all of the Windows Terminal and Console Host source, under the MIT license.
This commit is contained in:
192
src/host/readDataDirect.cpp
Normal file
192
src/host/readDataDirect.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "readDataDirect.hpp"
|
||||
#include "dbcs.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "..\interactivity\inc\ServiceLocator.hpp"
|
||||
|
||||
// Routine Description:
|
||||
// - Constructs direct read data class to hold context across sessions
|
||||
// generally when there's not enough data to return. Also used a bit
|
||||
// internally to just pass some information along the stack
|
||||
// (regardless of wait necessity).
|
||||
// Arguments:
|
||||
// - pInputBuffer - Buffer that data will be read from.
|
||||
// - pInputReadHandleData - Context stored across calls from the same
|
||||
// input handle to return partial data appropriately.
|
||||
// the user's buffer (pOutRecords)
|
||||
// - eventReadCount - the number of events to read
|
||||
// - partialEvents - any partial events already read
|
||||
// Return Value:
|
||||
// - THROW: Throws E_INVALIDARG for invalid pointers.
|
||||
DirectReadData::DirectReadData(_In_ InputBuffer* const pInputBuffer,
|
||||
_In_ INPUT_READ_HANDLE_DATA* const pInputReadHandleData,
|
||||
const size_t eventReadCount,
|
||||
_In_ std::deque<std::unique_ptr<IInputEvent>> partialEvents) :
|
||||
ReadData(pInputBuffer, pInputReadHandleData),
|
||||
_eventReadCount{ eventReadCount },
|
||||
_partialEvents{ std::move(partialEvents) },
|
||||
_outEvents{ }
|
||||
{
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Destructs a read data class.
|
||||
// - Decrements count of readers waiting on the given handle.
|
||||
DirectReadData::~DirectReadData()
|
||||
{
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This routine is called to complete a direct read that blocked in
|
||||
// ReadInputBuffer. The context of the read was saved in the DirectReadData
|
||||
// structure. This routine is called when events have been written to
|
||||
// the input buffer. It is called in the context of the writing thread.
|
||||
// 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 - Should we return UCS-2 unicode data, or should we
|
||||
// run the final data through the current Input Codepage before
|
||||
// returning?
|
||||
// - pReplyStatus - The status code to return to the client
|
||||
// application that originally called the API (before it was queued to
|
||||
// wait)
|
||||
// - pNumBytes - not used
|
||||
// - pControlKeyState - For certain types of reads, this specifies
|
||||
// which modifier keys were held.
|
||||
// - pOutputData - a pointer to a
|
||||
// std::deque<std::unique_ptr<IInputEvent>> that is used to the read
|
||||
// input events back to the server
|
||||
// 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 DirectReadData::Notify(const WaitTerminationReason TerminationReason,
|
||||
const bool fIsUnicode,
|
||||
_Out_ NTSTATUS* const pReplyStatus,
|
||||
_Out_ size_t* const pNumBytes,
|
||||
_Out_ DWORD* const pControlKeyState,
|
||||
_Out_ void* const pOutputData)
|
||||
{
|
||||
FAIL_FAST_IF_NULL(pOutputData);
|
||||
|
||||
FAIL_FAST_IF(_pInputReadHandleData->GetReadCount() == 0);
|
||||
|
||||
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
FAIL_FAST_IF(!gci.IsConsoleLocked());
|
||||
|
||||
*pReplyStatus = STATUS_SUCCESS;
|
||||
*pControlKeyState = 0;
|
||||
*pNumBytes = 0;
|
||||
bool retVal = true;
|
||||
std::deque<std::unique_ptr<IInputEvent>> readEvents;
|
||||
|
||||
// If ctrl-c or ctrl-break was seen, ignore it.
|
||||
if (WI_IsAnyFlagSet(TerminationReason, (WaitTerminationReason::CtrlC | WaitTerminationReason::CtrlBreak)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if a partial byte is already stored that we should read
|
||||
if (!fIsUnicode &&
|
||||
_pInputBuffer->IsReadPartialByteSequenceAvailable() &&
|
||||
_eventReadCount == 1)
|
||||
{
|
||||
_partialEvents.push_back(_pInputBuffer->FetchReadPartialByteSequence(false));
|
||||
}
|
||||
|
||||
// See if called by CsrDestroyProcess or CsrDestroyThread
|
||||
// via ConsoleNotifyWaitBlock. If so, just decrement the ReadCount and return.
|
||||
if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::ThreadDying))
|
||||
{
|
||||
*pReplyStatus = STATUS_THREAD_IS_TERMINATING;
|
||||
}
|
||||
// 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.
|
||||
else if (WI_IsFlagSet(TerminationReason, WaitTerminationReason::HandleClosing))
|
||||
{
|
||||
*pReplyStatus = STATUS_ALERTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we get to here, this routine was called either by the input
|
||||
// thread or a write routine. both of these callers grab the
|
||||
// current console lock.
|
||||
|
||||
// calculate how many events we need to read
|
||||
size_t amountAlreadyRead;
|
||||
if (FAILED(SizeTAdd(_partialEvents.size(), _outEvents.size(), &amountAlreadyRead)))
|
||||
{
|
||||
*pReplyStatus = STATUS_INTEGER_OVERFLOW;
|
||||
return retVal;
|
||||
}
|
||||
size_t amountToRead;
|
||||
if (FAILED(SizeTSub(_eventReadCount, amountAlreadyRead, &amountToRead)))
|
||||
{
|
||||
*pReplyStatus = STATUS_INTEGER_OVERFLOW;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
*pReplyStatus = _pInputBuffer->Read(readEvents,
|
||||
amountToRead,
|
||||
false,
|
||||
false,
|
||||
fIsUnicode,
|
||||
false);
|
||||
|
||||
if (*pReplyStatus == CONSOLE_STATUS_WAIT)
|
||||
{
|
||||
retVal = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (*pReplyStatus != CONSOLE_STATUS_WAIT)
|
||||
{
|
||||
// split key events to oem chars if necessary
|
||||
if (*pReplyStatus == STATUS_SUCCESS && !fIsUnicode)
|
||||
{
|
||||
try
|
||||
{
|
||||
SplitToOem(readEvents);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// combine partial and whole events
|
||||
while (!_partialEvents.empty())
|
||||
{
|
||||
readEvents.push_front(std::move(_partialEvents.back()));
|
||||
_partialEvents.pop_back();
|
||||
}
|
||||
|
||||
// move read events to out storage
|
||||
for (size_t i = 0; i < _eventReadCount; ++i)
|
||||
{
|
||||
if (readEvents.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
_outEvents.push_back(std::move(readEvents.front()));
|
||||
readEvents.pop_front();
|
||||
}
|
||||
|
||||
// store partial event if necessary
|
||||
if (!readEvents.empty())
|
||||
{
|
||||
_pInputBuffer->StoreReadPartialByteSequence(std::move(readEvents.front()));
|
||||
readEvents.pop_front();
|
||||
FAIL_FAST_IF(!(readEvents.empty()));
|
||||
}
|
||||
|
||||
// move events to pOutputData
|
||||
std::deque<std::unique_ptr<IInputEvent>>* const pOutputDeque = reinterpret_cast<std::deque<std::unique_ptr<IInputEvent>>* const>(pOutputData);
|
||||
*pNumBytes = _outEvents.size() * sizeof(INPUT_RECORD);
|
||||
pOutputDeque->swap(_outEvents);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
Reference in New Issue
Block a user