terminal/src/host/convarea.cpp

176 lines
6.3 KiB
C++
Raw Permalink Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "_output.h"
#include "../interactivity/inc/ServiceLocator.hpp"
#pragma hdrstop
using namespace Microsoft::Console::Types;
using Microsoft::Console::Interactivity::ServiceLocator;
bool IsValidSmallRect(_In_ PSMALL_RECT const Rect)
{
return (Rect->Right >= Rect->Left && Rect->Bottom >= Rect->Top);
}
void WriteConvRegionToScreen(const SCREEN_INFORMATION& ScreenInfo,
const Viewport& convRegion)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (!ScreenInfo.IsActiveScreenBuffer())
{
return;
}
ConsoleImeInfo* const pIme = &gci.ConsoleIme;
for (unsigned int i = 0; i < pIme->ConvAreaCompStr.size(); ++i)
{
const auto& ConvAreaInfo = pIme->ConvAreaCompStr[i];
if (!ConvAreaInfo.IsHidden())
{
const auto currentViewport = ScreenInfo.GetViewport().ToInclusive();
const auto areaInfo = ConvAreaInfo.GetAreaBufferInfo();
// Do clipping region
SMALL_RECT Region;
Region.Left = currentViewport.Left + areaInfo.rcViewCaWindow.Left + areaInfo.coordConView.X;
Region.Right = Region.Left + (areaInfo.rcViewCaWindow.Right - areaInfo.rcViewCaWindow.Left);
Region.Top = currentViewport.Top + areaInfo.rcViewCaWindow.Top + areaInfo.coordConView.Y;
Region.Bottom = Region.Top + (areaInfo.rcViewCaWindow.Bottom - areaInfo.rcViewCaWindow.Top);
SMALL_RECT ClippedRegion;
ClippedRegion.Left = std::max(Region.Left, currentViewport.Left);
ClippedRegion.Top = std::max(Region.Top, currentViewport.Top);
ClippedRegion.Right = std::min(Region.Right, currentViewport.Right);
ClippedRegion.Bottom = std::min(Region.Bottom, currentViewport.Bottom);
if (IsValidSmallRect(&ClippedRegion))
{
Region = ClippedRegion;
ClippedRegion.Left = std::max(Region.Left, convRegion.Left());
ClippedRegion.Top = std::max(Region.Top, convRegion.Top());
ClippedRegion.Right = std::min(Region.Right, convRegion.RightInclusive());
ClippedRegion.Bottom = std::min(Region.Bottom, convRegion.BottomInclusive());
if (IsValidSmallRect(&ClippedRegion))
{
// if we have a renderer, we need to update.
// we've already confirmed (above with an early return) that we're on conversion areas that are a part of the active (visible/rendered) screen
// so send invalidates to those regions such that we're queried for data on the next frame and repainted.
if (ServiceLocator::LocateGlobals().pRender != nullptr)
{
// convert inclusive rectangle to exclusive rectangle
SMALL_RECT srExclusive = ClippedRegion;
srExclusive.Right++;
srExclusive.Bottom++;
ServiceLocator::LocateGlobals().pRender->TriggerRedraw(Viewport::FromExclusive(srExclusive));
}
}
}
}
}
}
[[nodiscard]] HRESULT ConsoleImeResizeCompStrView()
{
try
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
ConsoleImeInfo* const pIme = &gci.ConsoleIme;
pIme->RedrawCompMessage();
}
CATCH_RETURN();
return S_OK;
}
[[nodiscard]] HRESULT ConsoleImeResizeCompStrScreenBuffer(const COORD coordNewScreenSize)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
ConsoleImeInfo* const pIme = &gci.ConsoleIme;
return pIme->ResizeAllAreas(coordNewScreenSize);
}
[[nodiscard]] HRESULT ImeStartComposition()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.LockConsole();
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
// MSFT:29219348 Some IME implementations do not produce composition strings, and
// their users have come to rely on the cursor that conhost traditionally left on
// until a composition string showed up.
// One such IME is WNWB's "Universal Wubi input method" from wnwb.com (v. 10+).
// We shouldn't hide the cursor here so as to not break those IMEs.
Make sure cursor visibility is restored after using an IME (#6207) ## Summary of the Pull Request When using an _Input Method Editor_ in conhost for East Asian languages, the text cursor is temporarily hidden while the characters are being composed. When the composition is complete, the cursor visibility is meant to be restored, but that doesn't always happen if the IME composition is cancelled. This PR makes sure the cursor visibility is always restored, regardless of how the IME is closed. ## PR Checklist * [x] Closes #810 * [x] CLA signed. * [ ] Tests added/passed * [ ] Requires documentation to be updated * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. ## Detailed Description of the Pull Request / Additional comments The original implementation hid the cursor whenever `ConsoleImeInfo::WriteCompMessage` was called (which could be multiple times in the course of a composition), and then only restored the visibility when `ConsoleImeInfo::WriteResultMessage` was called. If a composition is cancelled, though, `WriteResultMessage` would never be called, so the cursor visibility wouldn't be restored. I've now made the `SaveCursorVisibility` and `RestoreCursorVisibility` methods public, so they can instead be called from the `ImeStartComposition` and `ImeEndComposition` functions. This makes sure `RestoreCursorVisibility` is always called, regardless of how the composition ended, and `SaveCursorVisibility` is only called once at the start of the composition (which isn't essential, but seems cleaner to me). ## Validation Steps Performed I've manually tested opening and closing the IME, both while submitting characters and while cancelling a composition, and in all cases the cursor visibility was correctly restored.
2020-05-27 18:31:09 +02:00
gci.pInputBuffer->fInComposition = true;
return S_OK;
}
[[nodiscard]] HRESULT ImeEndComposition()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.LockConsole();
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
Make sure cursor visibility is restored after using an IME (#6207) ## Summary of the Pull Request When using an _Input Method Editor_ in conhost for East Asian languages, the text cursor is temporarily hidden while the characters are being composed. When the composition is complete, the cursor visibility is meant to be restored, but that doesn't always happen if the IME composition is cancelled. This PR makes sure the cursor visibility is always restored, regardless of how the IME is closed. ## PR Checklist * [x] Closes #810 * [x] CLA signed. * [ ] Tests added/passed * [ ] Requires documentation to be updated * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. ## Detailed Description of the Pull Request / Additional comments The original implementation hid the cursor whenever `ConsoleImeInfo::WriteCompMessage` was called (which could be multiple times in the course of a composition), and then only restored the visibility when `ConsoleImeInfo::WriteResultMessage` was called. If a composition is cancelled, though, `WriteResultMessage` would never be called, so the cursor visibility wouldn't be restored. I've now made the `SaveCursorVisibility` and `RestoreCursorVisibility` methods public, so they can instead be called from the `ImeStartComposition` and `ImeEndComposition` functions. This makes sure `RestoreCursorVisibility` is always called, regardless of how the composition ended, and `SaveCursorVisibility` is only called once at the start of the composition (which isn't essential, but seems cleaner to me). ## Validation Steps Performed I've manually tested opening and closing the IME, both while submitting characters and while cancelling a composition, and in all cases the cursor visibility was correctly restored.
2020-05-27 18:31:09 +02:00
ConsoleImeInfo* const pIme = &gci.ConsoleIme;
pIme->RestoreCursorVisibility();
gci.pInputBuffer->fInComposition = false;
return S_OK;
}
[[nodiscard]] HRESULT ImeComposeData(std::wstring_view text,
gsl::span<const BYTE> attributes,
gsl::span<const WORD> colorArray)
{
try
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.LockConsole();
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
ConsoleImeInfo* const pIme = &gci.ConsoleIme;
pIme->WriteCompMessage(text, attributes, colorArray);
}
CATCH_RETURN();
return S_OK;
}
[[nodiscard]] HRESULT ImeClearComposeData()
{
try
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.LockConsole();
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
ConsoleImeInfo* const pIme = &gci.ConsoleIme;
pIme->ClearAllAreas();
}
CATCH_RETURN();
return S_OK;
}
[[nodiscard]] HRESULT ImeComposeResult(std::wstring_view text)
{
try
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.LockConsole();
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
ConsoleImeInfo* const pIme = &gci.ConsoleIme;
pIme->WriteResultMessage(text);
}
CATCH_RETURN();
return S_OK;
}