Skip to content

Commit

Permalink
Split into snap-on-input and -output
Browse files Browse the repository at this point in the history
  • Loading branch information
lhecker committed Aug 5, 2024
1 parent f188293 commit 9adfccb
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 62 deletions.
5 changes: 5 additions & 0 deletions src/host/_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t
writer.Submit();
}

screenInfo.SnapOnOutput();
return;
}

Expand Down Expand Up @@ -328,6 +329,8 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t
{
writer.Submit();
}

screenInfo.SnapOnOutput();
}

// This is the main entrypoint for conhost to write VT to the buffer.
Expand Down Expand Up @@ -375,6 +378,8 @@ void WriteCharsVT(SCREEN_INFORMATION& screenInfo, const std::wstring_view& str)
write(offset, std::wstring_view::npos);
writer.Submit();
}

screenInfo.SnapOnOutput();
}

// Erases all contents of the given screenInfo, including the current screen and scrollback.
Expand Down
111 changes: 55 additions & 56 deletions src/host/screenInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1521,31 +1521,7 @@ void SCREEN_INFORMATION::ClipToScreenBuffer(_Inout_ til::inclusive_rect* const p

void SCREEN_INFORMATION::MakeCurrentCursorVisible()
{
MakeLocationVisibleWithSnap(_textBuffer->GetCursor().GetPosition());
}

static constexpr bool IsInputKey(WORD vkey)
{
return vkey != VK_CONTROL &&
vkey != VK_LCONTROL &&
vkey != VK_RCONTROL &&
vkey != VK_MENU &&
vkey != VK_LMENU &&
vkey != VK_RMENU &&
vkey != VK_SHIFT &&
vkey != VK_LSHIFT &&
vkey != VK_RSHIFT &&
vkey != VK_LWIN &&
vkey != VK_RWIN &&
vkey != VK_SNAPSHOT;
}

void SCREEN_INFORMATION::SnapOnInput(const WORD vkey)
{
if (IsInputKey(vkey))
{
MakeCurrentCursorVisible();
}
MakeCursorVisible(_textBuffer->GetCursor().GetPosition());
}

// Routine Description:
Expand Down Expand Up @@ -1693,30 +1669,41 @@ void SCREEN_INFORMATION::SetCursorDBMode(const bool DoubleCursor)
return STATUS_SUCCESS;
}

void SCREEN_INFORMATION::MakeLocationVisible(til::point position)
static constexpr bool IsInputKey(WORD vkey)
{
const auto viewportOrigin = _viewport.Origin();
const auto viewportSize = _viewport.Dimensions();
const auto bufferSize = _textBuffer->GetSize().Dimensions();
auto origin = viewportOrigin;

// Ensure the given position is in bounds.
position.x = std::clamp(position.x, 0, bufferSize.width - 1);
position.y = std::clamp(position.y, 0, bufferSize.height - 1);

origin.x = std::min(origin.x, position.x); // shift left if left
origin.x = std::max(origin.x, position.x - (viewportSize.width - 1)); // shift right if right
return vkey != VK_CONTROL &&
vkey != VK_LCONTROL &&
vkey != VK_RCONTROL &&
vkey != VK_MENU &&
vkey != VK_LMENU &&
vkey != VK_RMENU &&
vkey != VK_SHIFT &&
vkey != VK_LSHIFT &&
vkey != VK_RSHIFT &&
vkey != VK_LWIN &&
vkey != VK_RWIN &&
vkey != VK_SNAPSHOT;
}

origin.y = std::min(origin.y, position.y); // shift up if above
origin.y = std::max(origin.y, position.y - (viewportSize.height - 1)); // shift down if below
void SCREEN_INFORMATION::MakeCursorVisible(til::point position)
{
_makeLocationVisible(position, ViewportMovementMask::Vertical | ViewportMovementMask::Horizontal);
}

if (origin != viewportOrigin)
void SCREEN_INFORMATION::SnapOnInput(const WORD vkey)
{
if (IsInputKey(vkey))
{
std::ignore = SetViewportOrigin(true, origin, false);
_makeLocationVisible(_textBuffer->GetCursor().GetPosition(), ViewportMovementMask::Vertical);
}
}

void SCREEN_INFORMATION::MakeLocationVisibleWithSnap(til::point position)
void SCREEN_INFORMATION::SnapOnOutput()
{
_makeLocationVisible(_textBuffer->GetCursor().GetPosition(), ViewportMovementMask::HorizontalCenter);
}

void SCREEN_INFORMATION::_makeLocationVisible(til::point position, ViewportMovementMask movements)
{
const auto viewportOrigin = _viewport.Origin();
const auto viewportSize = _viewport.Dimensions();
Expand All @@ -1727,23 +1714,35 @@ void SCREEN_INFORMATION::MakeLocationVisibleWithSnap(til::point position)
position.x = std::clamp(position.x, 0, bufferSize.width - 1);
position.y = std::clamp(position.y, 0, bufferSize.height - 1);

// If the position is horizontally outside the viewport, snap the
// viewport to the nearest multiple of half the viewport width.
if (position.x < origin.x || position.x >= (origin.x + viewportSize.width))
if (WI_IsAnyFlagSet(movements, ViewportMovementMask::Vertical))
{
origin.y = std::min(origin.y, position.y); // shift up if above
origin.y = std::max(origin.y, position.y - (viewportSize.height - 1)); // shift down if below
}

if (WI_IsAnyFlagSet(movements, ViewportMovementMask::Horizontal))
{
const auto div = viewportSize.width / 2;
// We want our viewport to be centered around the position (= "half-width" offset).
// Since the origin is the left edge, we must subtract a half-width from the position.
origin.x = position.x - div;
// Round down to the nearest multiple of the viewport width.
// This also works if origin.x is negative, because the "modulo operator"
// is not a modulo operator, it's a remainder operator. The remainder of a
// negative number is negative and so origin.x cannot end up being less than 0.
origin.x -= origin.x % div;
origin.x = std::min(origin.x, position.x); // shift left if left
origin.x = std::max(origin.x, position.x - (viewportSize.width - 1)); // shift right if right
}

origin.y = std::min(origin.y, position.y); // shift up if above
origin.y = std::max(origin.y, position.y - (viewportSize.height - 1)); // shift down if below
if (WI_IsAnyFlagSet(movements, ViewportMovementMask::HorizontalCenter))
{
// If the position is horizontally outside the viewport, snap the
// viewport to the nearest multiple of half the viewport width.
if (position.x < origin.x || position.x >= (origin.x + viewportSize.width))
{
const auto div = viewportSize.width / 2;
// We want our viewport to be centered around the position (= "half-width" offset).
// Since the origin is the left edge, we must subtract a half-width from the position.
origin.x = position.x - div;
// Round down to the nearest multiple of the viewport width.
// This also works if origin.x is negative, because the "modulo operator"
// is not a modulo operator, it's a remainder operator. The remainder of a
// negative number is negative and so origin.x cannot end up being less than 0.
origin.x -= origin.x % div;
}
}

if (origin != viewportOrigin)
{
Expand Down
14 changes: 11 additions & 3 deletions src/host/screenInfo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ Revision History:
#include "../types/inc/Viewport.hpp"
class ConversionAreaInfo; // forward decl window. circular reference

enum class ViewportMovementMask
{
Vertical = 0x1,
Horizontal = 0x2,
HorizontalCenter = 0x4,
};
DEFINE_ENUM_FLAG_OPERATORS(ViewportMovementMask);

class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console::IIoProvider
{
public:
Expand All @@ -71,7 +79,9 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console
void GetRequiredConsoleSizeInPixels(_Out_ til::size* const pRequiredSize) const;

void MakeCurrentCursorVisible();
void MakeCursorVisible(til::point position);
void SnapOnInput(WORD vkey);
void SnapOnOutput();

void ClipToScreenBuffer(_Inout_ til::inclusive_rect* const psrClip) const;

Expand Down Expand Up @@ -185,9 +195,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console
void SetCursorDBMode(const bool DoubleCursor);
[[nodiscard]] NTSTATUS SetCursorPosition(const til::point Position, const bool TurnOn);

void MakeLocationVisible(til::point position);
void MakeLocationVisibleWithSnap(til::point position);

[[nodiscard]] NTSTATUS UseAlternateScreenBuffer(const TextAttribute& initAttributes);
void UseMainScreenBuffer();

Expand Down Expand Up @@ -237,6 +244,7 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console
void _CalculateViewportSize(const til::rect* const prcClientArea, _Out_ til::size* const pcoordSize);
void _AdjustViewportSize(const til::rect* const prcClientNew, const til::rect* const prcClientOld, const til::size* const pcoordSize);
void _InternalSetViewportSize(const til::size* pcoordSize, const bool fResizeFromTop, const bool fResizeFromLeft);
void _makeLocationVisible(til::point position, ViewportMovementMask movements);

static void s_CalculateScrollbarVisibility(const til::rect* const prcClientArea,
const til::size* const pcoordBufferSize,
Expand Down
4 changes: 2 additions & 2 deletions src/host/selection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ void Selection::ExtendSelection(_In_ til::point coordBufferPos)
}

// scroll if necessary to make cursor visible.
screenInfo.MakeLocationVisible(coordBufferPos);
screenInfo.MakeCursorVisible(coordBufferPos);

_dwSelectionFlags |= CONSOLE_SELECTION_NOT_EMPTY;
_srSelectionRect.left = _srSelectionRect.right = _coordSelectionAnchor.x;
Expand All @@ -227,7 +227,7 @@ void Selection::ExtendSelection(_In_ til::point coordBufferPos)
else
{
// scroll if necessary to make cursor visible.
screenInfo.MakeLocationVisible(coordBufferPos);
screenInfo.MakeCursorVisible(coordBufferPos);
}

// remember previous selection rect
Expand Down
2 changes: 1 addition & 1 deletion src/host/selectionInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ bool Selection::_HandleMarkModeSelectionNav(const INPUT_KEY_INFO* const pInputKe

cursor.SetHasMoved(true);
_coordSelectionAnchor = textBuffer.GetCursor().GetPosition();
ScreenInfo.MakeLocationVisible(_coordSelectionAnchor);
ScreenInfo.MakeCursorVisible(_coordSelectionAnchor);
_srSelectionRect.left = _srSelectionRect.right = _coordSelectionAnchor.x;
_srSelectionRect.top = _srSelectionRect.bottom = _coordSelectionAnchor.y;
}
Expand Down

0 comments on commit 9adfccb

Please sign in to comment.