Compare commits

...

8 Commits

Author SHA1 Message Date
Leonard Hecker
38e2a16607 wip 2024-11-26 00:46:06 +01:00
Leonard Hecker
adac6085b8 Defuse throttled_func when it's accidentally engaged (#18235)
This change prevents `throttled_func` from reading uninitialized memory
under some yet-unkown circumstances. The tl;dr is:
This simply moves the callback invocation into the storage.
That way we can centrally avoid invoking the callback accidentally.
2024-11-25 23:31:38 +00:00
Leonard Hecker
09d8ac44ad Fix order and robustness of CTRL_*_EVENTs (#18233)
* This fixes a regression in 391abafc, which caused attached clients
  to receive CTRL_CLOSE_EVENTs, etc., in oldest-to-newest order,
  while historically the opposite is expected.
* It also changes the behavior of `ProcessCtrlEvents` to dispatch
  these events no matter whether a client is already dead.
  This restores the Windows XP to Windows 8.1 behavior.

Both of these fixes would address the issue on their own.

Closes #15373

## Validation Steps Performed
* CloseTest from our repository shows newest-to-oldest order again.
* node gets killed when run under npm and closing the tab.
2024-11-25 17:05:30 -06:00
Leonard Hecker
220c7cd92e Revert "Dedup command history by default (#17852)" (#18229)
This reverts commit 5fdfd51209,
because 3 people complained about this change VS 1 person
requesting the change to be made in the first place.

Closes #18138
Reopens #17797 for discussion
2024-11-25 17:03:37 -06:00
PankajBhojwani
184dfdc5ea Fix UIMarkdown for Audit Mode (#18221) 2024-11-25 16:39:39 -06:00
AsciiWolf
919586c681 doc: Fix typo in CONTRIBUTING.md (#18243)
This PR fixes a very small typo (missing space) in the CONTRIBUTING.md
doc file.
2024-11-25 14:35:29 -08:00
Tsukasa OI
ae90d52bb1 Escape single quotes while translating dropped Win32 paths (#18007)
When file/folder is dropped to the terminal, its path is translated and
quoted with a pair of single quotes if necessary.

However, the terminal control does not escape single quotes (allowed in
the Win32 subsystem) that need escapes when translated.

On the translation styles other than `"none"` (note: all other
translation styles are currently intended for the POSIX shell), it
causes incorrect path to be pasted when the path contains one or more
single quotes (see #18006 for an example).

With this commit, the terminal control escapes a single quote with a
valid escape sequence `'\''` (finish quote, print a single quote then
begin quote again) when the path translation is required.

## History

### v1 → v2

* Changed escape sequence from `'"'"'` to much shorter `'\''`.
* Reflected comments by the reviewer.

### v2 → v3

* Overhaul after addition of multiple path translation styles (not just
WSL but Cygwin and MSYS).
* More clarification both in the code and in the commit message.

### v3 → v4 (current)

* Minor clarification both in the code and in the commit message.

## References and Relevant Issues

* #18006
* #16214
* #18195

## Detailed Description of the Pull Request / Additional comments

This is a follow-up of #16214 and #18195, fixing #18006.

Closes #18006
2024-11-23 12:34:21 -06:00
Dustin L. Howett
6047f37e84 build: fix arm64 vcpkg triplet selection error (#18239)
microsoft/vcpkg-tool#1474 now validates that the target triplet is
valid. Unfortunately, `ARM64` is not valid... despite VS defaulting to
it.

VS 17.12 moved to the newer version of the vcpkg tool.

Given that we still want to build on VS 17.12, this commit adds a local
workaround.

See DD-2302065 for the internal tracking bug.
 
See microsoft/vcpkg#42182 for the upstream fix.
2024-11-22 13:52:48 -08:00
21 changed files with 128 additions and 104 deletions

View File

@@ -158,7 +158,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and you
### Testing
Testing is a key component in the development workflow. Both Windows Terminal and Windows Console use TAEF(the Test Authoring and Execution Framework) as the main framework for testing.
Testing is a key component in the development workflow. Both Windows Terminal and Windows Console use TAEF (the Test Authoring and Execution Framework) as the main framework for testing.
If your changes affect existing test cases, or you're working on brand new features and also the accompanying test cases, see [TAEF](./doc/TAEF.md) for more information about how to validate your work locally.

View File

@@ -2304,10 +2304,14 @@ Global
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.Build.0 = Release|x64
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.ActiveCfg = Release|Win32
{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.Build.0 = Release|Win32
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|Any CPU.ActiveCfg = Debug|Win32
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|ARM64.ActiveCfg = Release|ARM64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x64.ActiveCfg = Release|x64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x86.ActiveCfg = Release|Win32
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|Any CPU.Build.0 = AuditMode|x64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x64.ActiveCfg = AuditMode|x64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x64.Build.0 = AuditMode|x64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x86.Build.0 = AuditMode|Win32
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|Any CPU.ActiveCfg = Debug|x64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|ARM64.ActiveCfg = Debug|ARM64
{7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|ARM64.Build.0 = Debug|ARM64

View File

@@ -66,6 +66,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
/* Cygwin */ L"/cygdrive/",
/* MSYS2 */ L"/",
};
static constexpr wil::zwstring_view sSingleQuoteEscape = L"'\\''";
static constexpr auto cchSingleQuoteEscape = sSingleQuoteEscape.size();
if (translationStyle == PathTranslationStyle::None)
{
@@ -75,6 +77,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// All of the other path translation modes current result in /-delimited paths
std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/');
// Escape single quotes, assuming translated paths are always quoted by a pair of single quotes.
size_t pos = 0;
while ((pos = fullPath.find(L'\'', pos)) != std::wstring::npos)
{
// ' -> '\'' (for POSIX shell)
fullPath.replace(pos, 1, sSingleQuoteEscape);
// Arithmetic overflow cannot occur here.
pos += cchSingleQuoteEscape;
}
if (fullPath.size() >= 2 && fullPath.at(1) == L':')
{
// C:/foo/bar -> Cc/foo/bar

View File

@@ -30,7 +30,7 @@ namespace winrt::Microsoft::Terminal::UI::Markdown::implementation
struct RequestRunCommandsArgs : RequestRunCommandsArgsT<RequestRunCommandsArgs>
{
RequestRunCommandsArgs(const winrt::hstring& commandlines) :
RequestRunCommandsArgs(const winrt::hstring& commandlines) noexcept :
Commandlines{ commandlines } {};
til::property<winrt::hstring> Commandlines;

View File

@@ -92,7 +92,7 @@ WUX::Documents::Paragraph MarkdownToXaml::_CurrentParagraph()
{
_lastParagraph.TextIndent(-WidthOfBulletPoint);
}
_lastParagraph.Margin(WUX::ThicknessHelper::FromLengths(IndentWidth * _indent, 0, 0, 0));
_lastParagraph.Margin(WUX::ThicknessHelper::FromLengths(static_cast<double>(IndentWidth) * _indent, 0, 0, 0));
}
_root.Blocks().Append(_lastParagraph);
}
@@ -137,16 +137,16 @@ WUX::Documents::Run MarkdownToXaml::_NewRun()
}
return _lastRun;
}
void MarkdownToXaml::_EndRun()
void MarkdownToXaml::_EndRun() noexcept
{
_lastRun = nullptr;
}
void MarkdownToXaml::_EndSpan()
void MarkdownToXaml::_EndSpan() noexcept
{
_EndRun();
_lastSpan = nullptr;
}
void MarkdownToXaml::_EndParagraph()
void MarkdownToXaml::_EndParagraph() noexcept
{
_EndSpan();
_lastParagraph = nullptr;
@@ -162,7 +162,7 @@ WUX::Controls::TextBlock MarkdownToXaml::_makeDefaultTextBlock()
void MarkdownToXaml::_RenderNode(cmark_node* node, cmark_event_type ev_type)
{
bool entering = (ev_type == CMARK_EVENT_ENTER);
const bool entering = (ev_type == CMARK_EVENT_ENTER);
switch (cmark_node_get_type(node))
{
@@ -221,7 +221,7 @@ void MarkdownToXaml::_RenderNode(cmark_node* node, cmark_event_type ev_type)
if (entering)
{
_EndParagraph();
_NewRun().Text(bullets[std::clamp(_indent - _blockQuoteDepth - 1, 0, 2)]);
_NewRun().Text(gsl::at(bullets, std::clamp(_indent - _blockQuoteDepth - 1, 0, 2)));
}
break;
@@ -251,7 +251,7 @@ void MarkdownToXaml::_RenderNode(cmark_node* node, cmark_event_type ev_type)
const auto codeHstring{ winrt::to_hstring(cmark_node_get_literal(node)) };
// The literal for a code node always includes the trailing newline.
// Trim that off.
std::wstring_view codeView{ codeHstring.c_str(), codeHstring.size() - 1 };
const std::wstring_view codeView{ codeHstring.c_str(), codeHstring.size() - 1 };
auto codeBlock = winrt::make<winrt::Microsoft::Terminal::UI::Markdown::implementation::CodeBlock>(winrt::hstring{ codeView });
WUX::Documents::InlineUIContainer codeContainer{};
@@ -282,7 +282,7 @@ void MarkdownToXaml::_RenderNode(cmark_node* node, cmark_event_type ev_type)
cmark_node* parent = cmark_node_parent(node);
cmark_node* grandparent = cmark_node_parent(parent);
if (grandparent != NULL && cmark_node_get_type(grandparent))
if (grandparent != nullptr && cmark_node_get_type(grandparent))
{
tight = cmark_node_get_list_tight(grandparent);
}

View File

@@ -27,9 +27,9 @@ private:
winrt::Windows::UI::Xaml::Documents::Run _CurrentRun();
winrt::Windows::UI::Xaml::Documents::Span _CurrentSpan();
winrt::Windows::UI::Xaml::Documents::Run _NewRun();
void _EndRun();
void _EndSpan();
void _EndParagraph();
void _EndRun() noexcept;
void _EndSpan() noexcept;
void _EndParagraph() noexcept;
winrt::Windows::UI::Xaml::Controls::TextBlock _makeDefaultTextBlock();

View File

@@ -113,7 +113,7 @@ private:
{
try
{
std::apply(self->_func, self->_storage.take());
self->_storage.apply(self->_func);
}
CATCH_LOG();
}

View File

@@ -208,7 +208,7 @@ public:
\
protected: \
type _##name{ __VA_ARGS__ }; \
void _set##name(const type& value) \
void _set##name(const type& value) noexcept(noexcept(_##name = value)) \
{ \
_##name = value; \
};

View File

@@ -275,6 +275,9 @@
<VcpkgOSTarget>windows</VcpkgOSTarget>
<VcpkgUseStatic>true</VcpkgUseStatic>
<!-- BODGY - work around DD#2302065. vcpkg now validates target triplets, and is *case sensitive* -->
<VcpkgPlatformTarget Condition="'$(Platform)'=='ARM64'">arm64</VcpkgPlatformTarget>
<VcpkgAdditionalInstallOptions>--x-feature=terminal</VcpkgAdditionalInstallOptions>
<!--
Since we link everything statically, we don't need to copy anything.

View File

@@ -321,28 +321,35 @@ void ProcessCtrlEvents()
return;
}
auto Status = STATUS_SUCCESS;
const auto ctrl = ServiceLocator::LocateConsoleControl();
for (const auto& r : termRecords)
{
/*
* 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 (SUCCEEDED_NTSTATUS(Status))
{
Status = ServiceLocator::LocateConsoleControl()
->EndTask(r.dwProcessID,
EventType,
CtrlFlags);
if (!r.hProcess)
{
Status = STATUS_SUCCESS;
}
}
// Older versions of Windows would do various things if the EndTask() call failed:
// * XP: Pops up a "Windows can't end this program" dialog for every already-dead process.
// * Vista - Win 7: Simply skips over already-dead processes.
// * Win 8 - Win 11 26100: Aborts on an already-dead process (you have to WM_CLOSE conhost multiple times).
//
// That last period had the following comment:
// 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.
//
// The corresponding logic worked like this:
// if (FAILED(EndTask(...)) && r.hProcess) {
// break;
// }
//
// That logic was removed around the Windows 11 26100 time frame, because CSRSS
// (which handles EndTask) now waits 5s and then force-kills the process for us.
// Going back to the Win 7 behavior then should make shutdown a lot more robust.
// The bad news is that EndTask() returns STATUS_UNSUCCESSFUL no matter whether
// the process was already dead, or if the request actually failed for some reason.
// Hopefully there aren't any regressions, but we can't know without trying.
LOG_IF_NTSTATUS_FAILED(ctrl->EndTask(r.dwProcessID, EventType, CtrlFlags));
}
}

View File

@@ -36,7 +36,7 @@ Settings::Settings() :
_bAutoPosition(true),
_uHistoryBufferSize(DEFAULT_NUMBER_OF_COMMANDS),
_uNumberOfHistoryBuffers(DEFAULT_NUMBER_OF_BUFFERS),
_bHistoryNoDup(true),
_bHistoryNoDup(false),
// ColorTable initialized below
_uCodePage(ServiceLocator::LocateGlobals().uiOEMCP),
_uScrollScale(1),
@@ -110,7 +110,7 @@ void Settings::ApplyDesktopSpecificDefaults()
_bQuickEdit = TRUE;
_uHistoryBufferSize = 50;
_uNumberOfHistoryBuffers = 4;
_bHistoryNoDup = true;
_bHistoryNoDup = FALSE;
_renderSettings.ResetColorTable();

View File

@@ -30,12 +30,22 @@ namespace til
}
}
std::tuple<Args...> take()
void apply(const auto& func)
{
std::unique_lock guard{ _lock };
auto pendingRunArgs = std::move(*_pendingRunArgs);
_pendingRunArgs.reset();
return pendingRunArgs;
decltype(_pendingRunArgs) args;
{
std::unique_lock guard{ _lock };
args = std::exchange(_pendingRunArgs, std::nullopt);
}
// Theoretically it should always have a value, because the throttled_func
// should not call the callback without there being a reason.
// But in practice a failure here was observed at least once.
// It's unknown to me what caused it, so the best we can do is avoid a crash.
assert(args.has_value());
if (args)
{
std::apply(func, *args);
}
}
explicit operator bool() const
@@ -60,10 +70,12 @@ namespace til
return _isPending.exchange(true, std::memory_order_relaxed);
}
std::tuple<> take()
void apply(const auto& func)
{
reset();
return {};
if (_isPending.exchange(false, std::memory_order_relaxed))
{
func();
}
}
void reset()
@@ -171,31 +183,24 @@ namespace til
void flush()
{
WaitForThreadpoolTimerCallbacks(_timer.get(), true);
if (_storage)
{
_trailing_edge();
}
_timer_callback(nullptr, this, nullptr);
}
private:
static void __stdcall _timer_callback(PTP_CALLBACK_INSTANCE /*instance*/, PVOID context, PTP_TIMER /*timer*/) noexcept
try
{
static_cast<throttled_func*>(context)->_trailing_edge();
}
CATCH_LOG()
void _trailing_edge()
{
const auto self = static_cast<throttled_func*>(context);
if constexpr (Leading)
{
_storage.reset();
self->_storage.reset();
}
else
{
std::apply(_func, _storage.take());
self->_storage.apply(self->_func);
}
}
CATCH_LOG()
wil::unique_threadpool_timer _createTimer()
{

View File

@@ -8,12 +8,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
template<typename T>
struct property
{
explicit constexpr property(auto&&... args) :
explicit constexpr property(auto&&... args) noexcept(std::is_nothrow_constructible_v<T, decltype(args)...>) :
_value{ std::forward<decltype(args)>(args)... } {}
property& operator=(const property& other) = default;
T operator()() const noexcept
T operator()() const noexcept(std::is_nothrow_copy_constructible<T>::value)
{
return _value;
}

View File

@@ -3,6 +3,9 @@
#include "precomp.h"
#include <dwmapi.h>
#include <uxtheme.h>
#include "../inc/conint.h"
using namespace Microsoft::Console::Internal;
@@ -21,7 +24,27 @@ using namespace Microsoft::Console::Internal;
return S_OK;
}
[[nodiscard]] HRESULT Theming::TrySetDarkMode(HWND /*hwnd*/) noexcept
[[nodiscard]] HRESULT Theming::TrySetDarkMode(HWND hwnd) noexcept
{
return S_FALSE;
static constexpr auto subKey = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize)";
static constexpr auto get = [](HKEY key, const wchar_t* value, DWORD& data) {
DWORD dataType = 0;
DWORD size = sizeof(data);
return RegGetValueW(key, subKey, value, RRF_RT_REG_DWORD, &dataType, &data, &size);
};
// This is the approach that WinUI3 used in its initial source code release at the time of writing.
DWORD useLightTheme = 0;
if (get(HKEY_CURRENT_USER, L"AppsUseLightTheme", useLightTheme) != ERROR_SUCCESS)
{
if (get(HKEY_LOCAL_MACHINE, L"SystemUsesLightTheme", useLightTheme) != ERROR_SUCCESS)
{
useLightTheme = 0;
}
}
const BOOL useDarkMode = useLightTheme == 0;
LOG_IF_FAILED(SetWindowTheme(hwnd, useLightTheme ? L"" : L"DarkMode_Explorer", nullptr));
LOG_IF_FAILED(DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &useDarkMode, sizeof(useDarkMode)));
return S_OK;
}

View File

@@ -79,7 +79,7 @@ VOID InitRegistryValues(
pStateInfo->CursorSize = 25;
pStateInfo->HistoryBufferSize = 25;
pStateInfo->NumberOfHistoryBuffers = 4;
pStateInfo->HistoryNoDup = 1;
pStateInfo->HistoryNoDup = 0;
// clang-format off
if (pStateInfo->fIsV2Console)

View File

@@ -211,9 +211,15 @@ ConsoleProcessHandle* ConsoleProcessList::GetOldestProcess() const
{
termRecords.clear();
// The caller (ProcessCtrlEvents) expects them in newest-to-oldest order,
// because that's how Windows has historically always dispatched these events.
auto it = _processes.crbegin();
const auto end = _processes.crend();
// Dig through known processes looking for a match
for (const auto& p : _processes)
for (; it != end; ++it)
{
const auto p = *it;
// If no limit was specified OR if we have a match, generate a new termination record.
if (!dwLimitingProcessId ||
p->_ulProcessGroupId == dwLimitingProcessId)

View File

@@ -1,19 +0,0 @@
#include "precomp.h"
#include "inc/ThemeUtils.h"
namespace Microsoft::Console::ThemeUtils
{
// Routine Description:
// - Attempts to enable/disable the dark mode on the frame of a window.
// Arguments:
// - hwnd: handle to the window to change
// - enabled: whether to enable or not the dark mode on the window's frame
// Return Value:
// - S_OK or suitable HRESULT from DWM engines.
[[nodiscard]] HRESULT SetWindowFrameDarkMode(HWND /* hwnd */, bool /* enabled */) noexcept
{
// TODO:GH #3425 implement the new DWM API and change
// src/interactivity/win32/windowtheme.cpp to use it.
return S_OK;
}
}

View File

@@ -1,8 +0,0 @@
#pragma once
#include <Windows.h>
namespace Microsoft::Console::ThemeUtils
{
[[nodiscard]] HRESULT SetWindowFrameDarkMode(HWND hwnd, bool enabled) noexcept;
}

View File

@@ -18,7 +18,6 @@
<ClCompile Include="..\GlyphWidth.cpp" />
<ClCompile Include="..\ScreenInfoUiaProviderBase.cpp" />
<ClCompile Include="..\sgrStack.cpp" />
<ClCompile Include="..\ThemeUtils.cpp" />
<ClCompile Include="..\UiaTextRangeBase.cpp" />
<ClCompile Include="..\UiaTracing.cpp" />
<ClCompile Include="..\TermControlUiaTextRange.cpp" />
@@ -38,7 +37,6 @@
<ClInclude Include="..\inc\GlyphWidth.hpp" />
<ClInclude Include="..\inc\IInputEvent.hpp" />
<ClInclude Include="..\inc\sgrStack.hpp" />
<ClInclude Include="..\inc\ThemeUtils.h" />
<ClInclude Include="..\inc\utils.hpp" />
<ClInclude Include="..\inc\Viewport.hpp" />
<ClInclude Include="..\IUiaEventDispatcher.h" />

View File

@@ -42,9 +42,6 @@
<ClCompile Include="..\UiaTextRangeBase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ThemeUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\sgrStack.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -107,9 +104,6 @@
<ClInclude Include="..\inc\utils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\ThemeUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\sgrStack.hpp">
<Filter>Header Files</Filter>
</ClInclude>

View File

@@ -35,7 +35,6 @@ SOURCES= \
..\convert.cpp \
..\colorTable.cpp \
..\utils.cpp \
..\ThemeUtils.cpp \
..\ScreenInfoUiaProviderBase.cpp \
..\sgrStack.cpp \
..\UiaTextRangeBase.cpp \