Compare commits

...

20 Commits

Author SHA1 Message Date
Leonard Hecker
38783fa595 Fix a ConPTY startup hang with 0-param DA1 responses (#18681)
Since `WaitForDA1` would wait until `_deviceAttributes` is non-zero,
we must ensure it's actually non-zero at the end of this handler,
even if there are no parameters.

## Validation Steps Performed
* Mod the Terminal DA1 to be `\x1b[?6c`. No hang 
* Mod the Terminal DA1 to be `\x1b[?61c`. No hang 

(cherry picked from commit 32ae00f71a)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgYO0a4
Service-Version: 1.23
2025-03-12 18:30:09 -05:00
Leonard Hecker
a0140ef644 Fix a handoff deadlock if layout completes synchronously (#18676)
I've received a dump from an affected user, and it showed that the
layout event in TerminalPage was raised synchronously. This meant that
during page initialization, the handoff listener was started while still
being stuck inside the handoff listener. This resulted in a deadlock.

This PR fixes the issue by not holding the lock across handoff callback
calls.

Closes #18634

## Validation Steps Performed
* Can't repro 

(cherry picked from commit 2693210ead)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgYKe9E PVTI_lADOAF3p4s4AxadtzgYLZvY
Service-Version: 1.23
2025-03-12 18:29:09 -05:00
Leonard Hecker
490e2bfc06 Fix dialogs not working across multiple windows (#18636)
This can be considered "part 1" of fixing #18599: It prevents crashes
(due to unhandled exceptions) by ensuring we only create 1 content
dialog across all windows at a time. Sounds bad, but I tried it and it's
not actually _that_ bad in practice (it's still really gross though).

The bad news is that I don't have a "part 2", because I can't figure out
what's going on:
* Create 2 windows
* Open the About dialog in window 1
  and right click the text
* Close the About dialog
* Open the About dialog in window 2
  and right click the text
* WinUI will simply toss the focus to window 1

It appears as if context menus are permanently associated with the first
window that uses them. It has nothing to do with whether a ContentDialog
instance is reused (I tested that).

## Validation Steps Performed
* Open 2 windows with 2 tabs each
* Attempt to close window 1, dialog appears 
* Attempt to close window 2, dialog moves to window 2 

(cherry picked from commit 3760caed97)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXyCU0
Service-Version: 1.23
2025-03-12 18:24:12 -05:00
Leonard Hecker
c1cd6d3d1d Fix bugs introduced in #18623 (#18635)
The logic didn't work when persistence was enabled and you had 2 windows
and closed the 2nd one, or when dragging the last tab out of the only
window.

## Validation Steps Performed
* 2 windows, close the 2nd one, app doesn't exit 
* 1 window, 1 tab, drag the tab out of the window, app doesn't exit 

(cherry picked from commit 96d1407c59)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXxggc
Service-Version: 1.23
2025-03-12 18:24:11 -05:00
Leonard Hecker
d938f924bb Fix persistence of the last closed window (#18623)
Does what it says on the tin.

Closes #18525

## Validation Steps Performed
* Enable persistence
* Close the last window
* Persisted 

(cherry picked from commit e1be2f4c73)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXpj9o
Service-Version: 1.23
2025-02-26 13:06:12 -06:00
Leonard Hecker
e9520c02ea Fix panes being dropped when tearing off tabs (#18627)
I don't actually know why this is happening, because it doesn't
happen with startup actions specified in the settings file.
In any case, it's fixed with more delays.

Closes #18572

## Validation Steps Performed
* Create a tab with 2 panes
* Tear it off into a new window
* New window has 1 tab with 2 panes 

(cherry picked from commit e1b28e72b3)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXTsJY PVTI_lADOAF3p4s4AxadtzgXsQ6w
Service-Version: 1.23
2025-02-26 13:06:10 -06:00
Carlos Zamora
2060fd6b11 Bugfix: don't round to nearest cell for mouse movements and VT mouse mode (#18602)
Missed a few `_getTerminalPosition()` on the first run. Disabled
rounding for pointer movements and mouse wheel events (which are used
for hyperlink hover detection and vt mouse mode). The only time we round
now is...
- `SetEndSelectionPoint()` --> because we're updating a selection
- `ControlCore->LeftClickOnTerminal()` --> where all paths are used for
selection*

*the only path that doesn't is `RepositionCursorWithMouse` being
enabled, which also makes sense based on clicking around Notepad with a
large font size.

## References and Relevant Issues
Follow-up for #18486
Closes #18595

## Validation Steps Performed
In large font size, play around with midnight commander and hover over
hyperlink edges.

(cherry picked from commit e5b972a828)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXgv1c
Service-Version: 1.23
2025-02-26 12:53:13 -06:00
Leonard Hecker
70996f35d0 Fix two sources of runtime exceptions (#18628)
* `_ApplyLanguageSettingChange` calls `PrimaryLanguageOverride`
  (the WinRT API function) and we would call it every time a new
  window is created. Now it's only called on settings load.
* `_RegisterTabEvents` would listen for "Content" changes which can
  be null. `IVector::Append` throws if a null object is given.
  In our case, it's null if the content got erased with nothing.

Additionally, this fixes a bug where we wouldn't call
`_ProcessLazySettingsChanges` on startup. This is important if the
settings file was changed while Windows Terminal wasn't running.

Lastly, there's a lifetime fix in this PR, which is a one-line change
and I didn't want to make a separate PR for that.

(cherry picked from commit ff9664d2d4)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXsQ60
Service-Version: 1.23
2025-02-26 12:53:12 -06:00
Leonard Hecker
a37dc26fd8 Reduce log spam on conhost exit (#18629)
When the server handle gets closed on conhost (= terminal is gone),
and e.g. PowerShell is used, we would previously log 6 error messages.
This PR reduces it to zero, by removing the 3 biggest offenders.

(cherry picked from commit 0df82681fe)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXsQ64
Service-Version: 1.23
2025-02-26 12:51:55 -06:00
Carlos Zamora
29e401f202 Add support for tabbing to embedded hyperlinks (#18347)
## Summary of the Pull Request
There's already logic to tab to a hyperlink when we're in mark mode. We
do this by looking at the automatically detected hyperlinks and finding
the next one of interest. This adds an extra step afterwards to find any
embedded hyperlinks and tab to them too.

Since embedded hyperlinks are stored as text attributes, we need to
iterate through the buffer to find the hyperlink and it's buffer
boundaries. This PR tries to reduce the workload of that by first
finding the automatically detected hyperlinks (since that's a fairly
quick process), then using the reduced search area to find the embedded
hyperlink (if one exists).

## Validation Steps Performed
In PowerShell, add an embedded hyperlink as such:
```powershell
${ESC}=[char]27
Write-Host "${ESC}]8;;https://github.com/microsoft/terminal${ESC}\This is a link!${ESC}]8;;${ESC}\"
```
Enter mark mode (ctrl+shift+m) then shift+tab to it.
 The "This is a link!" is selected
 Verified that this works when searching forwards and backwards

Closes #18310
Closes #15194
Follow-up from #13405
OSC 8 support added in #7251

(cherry picked from commit 35bd60782f)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXBDb8
Service-Version: 1.23
2025-02-26 12:51:54 -06:00
Leonard Hecker
0d7e1293fa Fix a crash when closing tabs (#18620)
WinUI asynchronously updates its tab view items, so it may happen that
we're given a `TabViewItem` that still contains a `TabBase` which has
actually already been removed. Regressed in #15924.

Closes #18581

## Validation Steps Performed
* Close tabs rapidly with middle click
* No crash 

(cherry picked from commit 62e7f4bfad)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXpjpM PVTI_lADOAF3p4s4AxadtzgXsRQM
Service-Version: 1.23
2025-02-26 12:51:53 -06:00
Leonard Hecker
a72531014c Fix persistence of empty windows (#18622)
This is a theoretical fix for #18584 as I cannot reproduce the issue
anymore. It did happen briefly on one of my devices though, and at the
time I observed that it would persist a window with no startup actions.

(cherry picked from commit 265d841509)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXpj1U
Service-Version: 1.23
2025-02-26 12:51:52 -06:00
Leonard Hecker
2590ff1383 Fix leaking the contents of the first tab in the first window (#18621)
Found this one completely randomly.

## Validation Steps Performed
* Open 2 windows with 1 tab each
* Click the X button on the tab in the 1st window
* OpenConsole/etc. is cleaned up 

---------

Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
(cherry picked from commit c7f0d0addb)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXpjzk
Service-Version: 1.23
2025-02-26 12:51:51 -06:00
Leonard Hecker
863cdd44f2 ConPTY: Fix shutdown if killed during startup (#18588)
During startup we relinquish ownership of the console lock to wait for
the DA1 response of the hosting terminal. The problem occurs if the
hosting terminal disconnects during that time. The broken pipe will
cause `VtIo` to send out `CTRL_CLOSE_EVENT` messages, but those won't
achieve anything, because the first and only client hasn't even finished
connecting yet. What we need to do instead is to return an error code.

In order to not use a bunch of booleans to control this behavior, I gave
`VtIo` a state enum. This however required restructuring the calling
code in order to not have a dozen states.

## Validation Steps Performed
* Launch cmd.exe with ConPTY
* ...but leave the stdin pipe unbound (which will hang the DA1 request)
* Immediately kill the ConPTY session
* cmd.exe exits after clicking away the error message 

(cherry picked from commit 733a5e7bec)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXjzIg
Service-Version: 1.23
2025-02-26 12:51:50 -06:00
Windows Console Service Bot
73721c7a90 Localization Updates - main - 02/13/2025 03:05:04 (#18569)
(cherry picked from commit 13e7c9314d)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXeZ1U
Service-Version: 1.23
2025-02-26 12:51:49 -06:00
Windows Console Service Bot
deeba28fda Localization Updates - Use Scheme Color
(cherry picked from commit f28f65870a)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXInUI
Service-Version: 1.23
2025-02-26 12:51:48 -06:00
Carlos Zamora
b43e7b93ec Fix missing icon and truncated text on NullableColorPicker (#18476)
Fixes an issue on Windows 10 where icon on selected color chips would be
missing in the NullableColorPicker.

Fixes (or at least significantly improves the experience) text being
truncated for the special colors in the NullableColorPicker. This was
done by removing the word "Use" from the labels and adding a visual
state trigger to change the layout of the chips and buttons when the
window becomes narrow.

Related to #18318

(cherry picked from commit 7423dd3b2a)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgXARtg
Service-Version: 1.23
2025-02-04 16:27:03 -06:00
Leonard Hecker
c6e20e99d7 Add Oklab based color scheme "Ottosson" (#18502)
Campbell has been the default color scheme for a long time now,
but it has quite some issues with hue and chroma.

This PR introduces a new scheme which was created using the Oklab
color space to find colors with maximal distance to each other
and well distributed and consistent hue and chroma.
Because of this, I've named the scheme after the creator of Oklab.
 
Closes #17818

(cherry picked from commit e60acbc12a)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgW-IC0
Service-Version: 1.23
2025-02-03 17:14:14 -06:00
Éléa Dufresne
8b0fc20f83 Fix Ctrl+Insert does not copy the selected text from Command Palette (#18483)
Fixes an issue where pressing `CTRL` + `Insert` does not copy text
selected in the Command Palette. Instead, it closes it, and any text
selected in the pane is copied to the clipboard.

Since `Insert` is a virtual key, I address the issue by adding a
conditional check for `CTRL` with either `Insert` or `C` (previously, it
only checked for `CTRL` with `C`) for the copy action in the Command
Palette.

## Validation Steps Performed

I followed the reproduction steps and verified that the actual behaviour
matched the expected behaviour. All existing tests passed, but no new
test was added.

Closes #9520

(cherry picked from commit 2e92a15464)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgW9xcU
Service-Version: 1.23
2025-02-03 15:21:29 -06:00
Windows Console Service Bot
77638840e4 Localization Updates - main - 01/31/2025 03:04:35 (#18484)
(cherry picked from commit aafbd17f3d)
Service-Card-Id: PVTI_lADOAF3p4s4AxadtzgW9xM8
Service-Version: 1.23
2025-02-03 13:42:44 -06:00
50 changed files with 685 additions and 438 deletions

View File

@@ -287,7 +287,7 @@ namespace TerminalAppLocalTests
NewTabArgs args{ newTerminalArgs };
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
// push the arg onto the front
page->_startupActions.Append(newTabAction);
page->_startupActions.push_back(std::move(newTabAction));
Log::Comment(L"Added a single newTab action");
auto app = ::winrt::Windows::UI::Xaml::Application::Current();

View File

@@ -752,13 +752,11 @@ namespace winrt::TerminalApp::implementation
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<ExecuteCommandlineArgs>())
{
auto actions = winrt::single_threaded_vector<ActionAndArgs>(
TerminalPage::ConvertExecuteCommandlineToActions(realArgs));
if (actions.Size() != 0)
auto actions = ConvertExecuteCommandlineToActions(realArgs);
if (!actions.empty())
{
actionArgs.Handled(true);
ProcessStartupActions(actions, false);
ProcessStartupActions(std::move(actions), false);
}
}
}

View File

@@ -184,8 +184,6 @@ namespace winrt::TerminalApp::implementation
// this as a MTA, before the app is Create()'d
WINRT_ASSERT(_loadedInitialSettings);
_ApplyLanguageSettingChange();
TraceLoggingWrite(
g_hTerminalAppProvider,
"AppCreated",
@@ -435,6 +433,10 @@ namespace winrt::TerminalApp::implementation
_settings.LogSettingChanges(true);
}
_ApplyLanguageSettingChange();
_ApplyStartupTaskStateChange();
_ProcessLazySettingsChanges();
if (initialLoad)
{
// Register for directory change notification.
@@ -445,10 +447,6 @@ namespace winrt::TerminalApp::implementation
// Here, we successfully reloaded the settings, and created a new
// TerminalSettings object.
_ApplyLanguageSettingChange();
_ApplyStartupTaskStateChange();
_ProcessLazySettingsChanges();
auto warnings{ winrt::multi_threaded_vector<SettingsLoadWarnings>() };
for (auto&& warn : _warnings)
{

View File

@@ -359,7 +359,7 @@ namespace winrt::TerminalApp::implementation
_switchToMode(CommandPaletteMode::CommandlineMode);
e.Handled(true);
}
else if (key == VirtualKey::C && ctrlDown)
else if ((key == VirtualKey::C || key == VirtualKey::Insert) && ctrlDown)
{
_searchBox().CopySelectionToClipboard();
e.Handled(true);

View File

@@ -206,7 +206,7 @@
<value>ウィンドウを閉じる</value>
</data>
<data name="SplitTabText" xml:space="preserve">
<value>[分割] タブ</value>
<value>タブを分割</value>
</data>
<data name="SplitPaneText" xml:space="preserve">
<value>ウィンドウを分割する</value>
@@ -224,7 +224,7 @@
<value>リセット</value>
</data>
<data name="RenameTabText" xml:space="preserve">
<value>[名前の変更] タブ</value>
<value>タブ名を変更</value>
</data>
<data name="DuplicateTabText" xml:space="preserve">
<value>タブを複製する</value>

View File

@@ -32,6 +32,7 @@ namespace winrt::TerminalApp::implementation
{
ASSERT_UI_THREAD();
// NOTE: `TerminalPage::_HandleCloseTabRequested` relies on the content being null after this call.
Content(nullptr);
}

View File

@@ -158,6 +158,8 @@ namespace winrt::TerminalApp::implementation
// Set this tab's icon to the icon from the content
_UpdateTabIcon(*newTabImpl);
// This is necessary, because WinUI does not have support for middle clicks.
// Its Tapped event doesn't provide the information what button was used either.
tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabPointerPressed });
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabPointerReleased });
tabViewItem.PointerExited({ this, &TerminalPage::_OnTabPointerExited });
@@ -903,19 +905,39 @@ namespace winrt::TerminalApp::implementation
if (_tabPointerMiddleButtonPressed && !eventArgs.GetCurrentPoint(nullptr).Properties().IsMiddleButtonPressed())
{
_tabPointerMiddleButtonPressed = false;
if (const auto tabViewItem{ sender.try_as<MUX::Controls::TabViewItem>() })
if (auto tabViewItem{ sender.try_as<MUX::Controls::TabViewItem>() })
{
tabViewItem.ReleasePointerCapture(eventArgs.Pointer());
auto tab = _GetTabByTabViewItem(tabViewItem);
if (!_tabPointerMiddleButtonExited && tab)
if (!_tabPointerMiddleButtonExited)
{
_HandleCloseTabRequested(tab);
_OnTabPointerReleasedCloseTab(std::move(tabViewItem));
}
}
eventArgs.Handled(true);
}
}
safe_void_coroutine TerminalPage::_OnTabPointerReleasedCloseTab(winrt::Microsoft::UI::Xaml::Controls::TabViewItem sender)
{
const auto tab = _GetTabByTabViewItem(sender);
if (!tab)
{
co_return;
}
// WinUI asynchronously updates its tab view items, so it may happen that we're given a
// `TabViewItem` that still contains a `TabBase` which has actually already been removed.
// First we must yield once, to flush out whatever TabView is currently doing.
const auto strong = get_strong();
co_await wil::resume_foreground(Dispatcher());
// `tab.Shutdown()` in `_RemoveTab()` sets the content to null = This checks if the tab is closed.
if (tab.Content())
{
_HandleCloseTabRequested(tab);
}
}
// Method Description:
// - Tracking pointer state for tab remove
// Arguments:

View File

@@ -51,8 +51,10 @@ namespace winrt::TerminalApp::implementation
_tabStatusChangedRevoker = status.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& /*sender*/, auto& /*e*/) {
// Sometimes nested bindings do not get updated,
// thus let's notify property changed on TabStatus when one of its properties changes
auto item{ weakThis.get() };
item->PropertyChanged.raise(*item, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"TabStatus" });
if (auto item{ weakThis.get() })
{
item->PropertyChanged.raise(*item, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"TabStatus" });
}
});
}
}

View File

@@ -62,7 +62,6 @@ namespace winrt::TerminalApp::implementation
TerminalPage::TerminalPage(TerminalApp::WindowProperties properties, const TerminalApp::ContentManager& manager) :
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
_mruTabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
_startupActions{ winrt::single_threaded_vector<ActionAndArgs>() },
_manager{ manager },
_hostingHwnd{},
_WindowProperties{ std::move(properties) }
@@ -297,7 +296,7 @@ namespace winrt::TerminalApp::implementation
// GH#12267: Don't forget about defterm handoff here. If we're being
// created for embedding, then _yea_, we don't need to handoff to an
// elevated window.
if (!_startupActions || IsRunningElevated() || _shouldStartInboundListener || _startupActions.Size() == 0)
if (_startupActions.empty() || IsRunningElevated() || _shouldStartInboundListener)
{
// there aren't startup actions, or we're elevated. In that case, go for it.
return false;
@@ -375,7 +374,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::HandoffToElevated(const CascadiaSettings& settings)
{
if (!_startupActions)
if (_startupActions.empty())
{
return;
}
@@ -489,7 +488,7 @@ namespace winrt::TerminalApp::implementation
{
_startupState = StartupState::InStartup;
ProcessStartupActions(_startupActions, true);
ProcessStartupActions(std::move(_startupActions), true);
// If we were told that the COM server needs to be started to listen for incoming
// default application connections, start it now.
@@ -546,80 +545,56 @@ namespace winrt::TerminalApp::implementation
// nt -d .` from inside another directory to work as expected.
// Return Value:
// - <none>
safe_void_coroutine TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector<ActionAndArgs> actions,
const bool initial,
const winrt::hstring cwd,
const winrt::hstring env)
safe_void_coroutine TerminalPage::ProcessStartupActions(std::vector<ActionAndArgs> actions, const bool initial, const winrt::hstring cwd, const winrt::hstring env)
{
auto weakThis{ get_weak() };
// Handle it on a subsequent pass of the UI thread.
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal);
const auto strong = get_strong();
// If the caller provided a CWD, "switch" to that directory, then switch
// back once we're done. This looks weird though, because we have to set
// up the scope_exit _first_. We'll release the scope_exit if we don't
// actually need it.
// back once we're done.
auto originalVirtualCwd{ _WindowProperties.VirtualWorkingDirectory() };
auto restoreCwd = wil::scope_exit([&originalVirtualCwd, this]() {
// ignore errors, we'll just power on through. We'd rather do
// something rather than fail silently if the directory doesn't
// actually exist.
_WindowProperties.VirtualWorkingDirectory(originalVirtualCwd);
});
// Literally the same thing with env vars too
auto originalVirtualEnv{ _WindowProperties.VirtualEnvVars() };
auto restoreEnv = wil::scope_exit([&originalVirtualEnv, this]() {
_WindowProperties.VirtualEnvVars(originalVirtualEnv);
auto restoreCwd = wil::scope_exit([&]() {
if (!cwd.empty())
{
// ignore errors, we'll just power on through. We'd rather do
// something rather than fail silently if the directory doesn't
// actually exist.
_WindowProperties.VirtualWorkingDirectory(originalVirtualCwd);
_WindowProperties.VirtualEnvVars(originalVirtualEnv);
}
});
_WindowProperties.VirtualWorkingDirectory(cwd);
_WindowProperties.VirtualEnvVars(env);
if (cwd.empty())
for (size_t i = 0; i < actions.size(); ++i)
{
// We didn't actually need to change the virtual CWD, so we don't
// need to restore it
restoreCwd.release();
}
else
{
_WindowProperties.VirtualWorkingDirectory(cwd);
}
if (env.empty())
{
restoreEnv.release();
}
else
{
_WindowProperties.VirtualEnvVars(env);
}
if (auto page{ weakThis.get() })
{
for (const auto& action : actions)
if (i != 0)
{
if (auto page{ weakThis.get() })
{
_actionDispatch->DoAction(action);
}
else
{
co_return;
}
// Each action may rely on the XAML layout of a preceding action.
// Most importantly, this is the case for the combination of NewTab + SplitPane,
// as the former appears to only have a layout size after at least 1 resume_foreground,
// while the latter relies on that information. This is also why it uses Low priority.
//
// Curiously, this does not seem to be required when using startupActions, but only when
// tearing out a tab (this currently creates a new window with injected startup actions).
// This indicates that this is really more of an architectural issue and not a fundamental one.
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Low);
}
// GH#6586: now that we're done processing all startup commands,
// focus the active control. This will work as expected for both
// commandline invocations and for `wt` action invocations.
if (const auto& terminalTab{ _GetFocusedTabImpl() })
_actionDispatch->DoAction(actions[i]);
}
// GH#6586: now that we're done processing all startup commands,
// focus the active control. This will work as expected for both
// commandline invocations and for `wt` action invocations.
if (const auto& terminalTab{ _GetFocusedTabImpl() })
{
if (const auto& content{ terminalTab->GetActiveContent() })
{
if (const auto& content{ terminalTab->GetActiveContent() })
{
content.Focus(FocusState::Programmatic);
}
content.Focus(FocusState::Programmatic);
}
}
if (initial)
{
_CompleteInitialization();
@@ -1783,16 +1758,22 @@ namespace winrt::TerminalApp::implementation
auto tab{ weakTab.get() };
if (page && tab)
{
if (args.PropertyName() == L"Title")
const auto propertyName = args.PropertyName();
if (propertyName == L"Title")
{
page->_UpdateTitle(*tab);
}
else if (args.PropertyName() == L"Content")
else if (propertyName == L"Content")
{
if (*tab == page->_GetFocusedTab())
{
page->_tabContent.Children().Clear();
page->_tabContent.Children().Append(tab->Content());
const auto children = page->_tabContent.Children();
children.Clear();
if (auto content = tab->Content())
{
page->_tabContent.Children().Append(std::move(content));
}
tab->Focus(FocusState::Programmatic);
}
@@ -1988,6 +1969,12 @@ namespace winrt::TerminalApp::implementation
actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end()));
}
// Avoid persisting a window with zero tabs, because `BuildStartupActions` happened to return an empty vector.
if (actions.empty())
{
return;
}
// if the focused tab was not the last tab, restore that
auto idx = _GetFocusedTabIndex();
if (idx && idx != tabCount - 1)
@@ -3658,13 +3645,9 @@ namespace winrt::TerminalApp::implementation
// - actions: a list of Actions to process on startup.
// Return Value:
// - <none>
void TerminalPage::SetStartupActions(std::vector<ActionAndArgs>& actions)
void TerminalPage::SetStartupActions(std::vector<ActionAndArgs> actions)
{
// The fastest way to copy all the actions out of the std::vector and
// put them into a winrt::IVector is by making a copy, then moving the
// copy into the winrt vector ctor.
auto listCopy = actions;
_startupActions = winrt::single_threaded_vector<ActionAndArgs>(std::move(listCopy));
_startupActions = std::move(actions);
}
// Routine Description:

View File

@@ -128,7 +128,7 @@ namespace winrt::TerminalApp::implementation
void Maximized(bool newMaximized);
void RequestSetMaximized(bool newMaximized);
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> actions);
void SetInboundListener(bool isEmbedding);
static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
@@ -146,7 +146,7 @@ namespace winrt::TerminalApp::implementation
void ActionSaveFailed(winrt::hstring message);
void ShowTerminalWorkingDirectory();
safe_void_coroutine ProcessStartupActions(Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> actions,
safe_void_coroutine ProcessStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> actions,
const bool initial,
const winrt::hstring cwd = winrt::hstring{},
const winrt::hstring env = winrt::hstring{});
@@ -255,7 +255,7 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::Controls::Grid::LayoutUpdated_revoker _layoutUpdatedRevoker;
StartupState _startupState{ StartupState::NotInitialized };
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
bool _shouldStartInboundListener{ false };
bool _isEmbeddingInboundListener{ false };
@@ -433,6 +433,7 @@ namespace winrt::TerminalApp::implementation
bool _tabPointerMiddleButtonExited{ false };
void _OnTabPointerPressed(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
void _OnTabPointerReleased(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
safe_void_coroutine _OnTabPointerReleasedCloseTab(winrt::Microsoft::UI::Xaml::Controls::TabViewItem sender);
void _OnTabPointerEntered(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
void _OnTabPointerExited(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);

View File

@@ -851,6 +851,9 @@ namespace winrt::TerminalApp::implementation
{
ASSERT_UI_THREAD();
// Don't forget to call the overridden function. :)
TabBase::Shutdown();
if (_rootPane)
{
_rootPane->Shutdown();

View File

@@ -144,7 +144,6 @@ namespace winrt::TerminalApp::implementation
{
// Now that we know we can do XAML, build our page.
_root = winrt::make_self<TerminalPage>(*_WindowProperties, _manager);
_dialog = ContentDialog{};
// Pass in information about the initial state of the window.
// * If we were supposed to start from serialized "content", do that,
@@ -313,6 +312,15 @@ namespace winrt::TerminalApp::implementation
{
return _settings.GlobalSettings().CurrentTheme();
}
// WinUI can't show 2 dialogs simultaneously. Yes, really. If you do, you get an exception.
// As such, we must dismiss whatever dialog is currently being shown.
//
// This limit is of course per-thread and not per-window. Yes... really. See:
// https://github.com/microsoft/microsoft-ui-xaml/issues/794
// The consequence is that we use a static variable to keep track of the shown dialog.
static ContentDialog s_activeDialog{ nullptr };
// Method Description:
// - Show a ContentDialog with buttons to take further action. Uses the
// FrameworkElements provided as the title and content of this dialog, and
@@ -328,16 +336,32 @@ namespace winrt::TerminalApp::implementation
// - an IAsyncOperation with the dialog result
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalWindow::ShowDialog(winrt::WUX::Controls::ContentDialog dialog)
{
// DON'T release this lock in a wil::scope_exit. The scope_exit will get
// called when we await, which is not what we want.
std::unique_lock lock{ _dialogLock, std::try_to_lock };
if (!lock)
// As mentioned on s_activeDialog, dismissing the active dialog is necessary.
// We repeat it a few times in case the resume_foreground failed to work,
// but I found that one iteration will always be enough in practice.
for (int i = 0; i < 3; ++i)
{
if (!s_activeDialog)
{
break;
}
s_activeDialog.Hide();
// Wait for the current dialog to be hidden.
co_await wil::resume_foreground(_root->Dispatcher(), CoreDispatcherPriority::Low);
}
// If two sources call ShowDialog() simultaneously, it may happen that both enter the above loop,
// but it's crucial that only one of them continues below as only 1 dialog can be shown at a time.
// Thankfully, everything runs on the UI thread, so only 1 caller will exit the above loop at a time.
// So, if s_activeDialog is still set at this point, we must have lost the race.
if (s_activeDialog)
{
// Another dialog is visible.
co_return ContentDialogResult::None;
}
_dialog = dialog;
s_activeDialog = dialog;
// IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs.
// Since we're hosting the dialog in a Xaml island, we need to connect it to the
@@ -367,23 +391,26 @@ namespace winrt::TerminalApp::implementation
}
} };
themingLambda(dialog, nullptr); // if it's already in the tree
auto loadedRevoker{ dialog.Loaded(winrt::auto_revoke, themingLambda) }; // if it's not yet in the tree
auto result = ContentDialogResult::None;
// Display the dialog.
co_return co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
// Extra scope to drop the revoker before resetting the s_activeDialog to null.
{
themingLambda(dialog, nullptr); // if it's already in the tree
auto loadedRevoker{ dialog.Loaded(winrt::auto_revoke, themingLambda) }; // if it's not yet in the tree
result = co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
}
// After the dialog is dismissed, the dialog lock (held by `lock`) will
// be released so another can be shown
s_activeDialog = nullptr;
co_return result;
}
// Method Description:
// - Dismiss the (only) visible ContentDialog
void TerminalWindow::DismissDialog()
{
if (auto localDialog = std::exchange(_dialog, nullptr))
if (s_activeDialog)
{
localDialog.Hide();
s_activeDialog.Hide();
}
}
@@ -1054,12 +1081,8 @@ namespace winrt::TerminalApp::implementation
{
_contentBounds = bounds;
const auto& args = _contentStringToActions(content, true);
for (const auto& action : args)
{
_initialContentArgs.push_back(action);
}
const auto args = _contentStringToActions(content, true);
_initialContentArgs = wil::to_vector(args);
}
// Method Description:
@@ -1085,7 +1108,7 @@ namespace winrt::TerminalApp::implementation
if (_appArgs->ExitCode() == 0)
{
auto& parsedArgs = _appArgs->ParsedArgs();
auto actions = winrt::single_threaded_vector<ActionAndArgs>(std::move(parsedArgs.GetStartupActions()));
auto& actions = parsedArgs.GetStartupActions();
_root->ProcessStartupActions(actions, false, _appArgs->CurrentDirectory(), _appArgs->CurrentEnvironment());
@@ -1200,7 +1223,7 @@ namespace winrt::TerminalApp::implementation
{
try
{
const auto& args = ActionAndArgs::Deserialize(content);
const auto args = ActionAndArgs::Deserialize(content);
if (args == nullptr ||
args.Size() == 0)
{
@@ -1244,9 +1267,9 @@ namespace winrt::TerminalApp::implementation
const bool replaceFirstWithNewTab = tabIndex >= _root->NumberOfTabs();
const auto& args = _contentStringToActions(content, replaceFirstWithNewTab);
auto args = _contentStringToActions(content, replaceFirstWithNewTab);
_root->AttachContent(args, tabIndex);
_root->AttachContent(std::move(args), tabIndex);
}
}
void TerminalWindow::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args)

View File

@@ -167,8 +167,6 @@ namespace winrt::TerminalApp::implementation
// ALSO: If you add any UIElements as roots here, make sure they're
// updated in _ApplyTheme. The root currently is _root.
winrt::com_ptr<TerminalPage> _root{ nullptr };
winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog{ nullptr };
std::shared_mutex _dialogLock;
wil::com_ptr<CommandlineArgs> _appArgs{ nullptr };
bool _hasCommandLineArguments{ false };

View File

@@ -14,6 +14,13 @@ static DWORD g_cTerminalHandoffRegistration = 0;
// Mutex so we only do start/stop/establish one at a time.
static std::shared_mutex _mtx;
// This is the callback that will be called when a connection is received.
// Call this once during startup and don't ever change it again (race condition).
void CTerminalHandoff::s_setCallback(NewHandoffFunction callback) noexcept
{
_pfnHandoff = callback;
}
// Routine Description:
// - Starts listening for TerminalHandoff requests by registering
// our class and interface with COM.
@@ -21,24 +28,19 @@ static std::shared_mutex _mtx;
// - pfnHandoff - Function to callback when a handoff is received
// Return Value:
// - S_OK, E_NOT_VALID_STATE (start called when already started) or relevant COM registration error.
HRESULT CTerminalHandoff::s_StartListening(NewHandoffFunction pfnHandoff)
HRESULT CTerminalHandoff::s_StartListening()
try
{
std::unique_lock lock{ _mtx };
RETURN_HR_IF(E_NOT_VALID_STATE, _pfnHandoff != nullptr);
const auto classFactory = Make<SimpleClassFactory<CTerminalHandoff>>();
RETURN_IF_NULL_ALLOC(classFactory);
RETURN_LAST_ERROR_IF_NULL(classFactory);
ComPtr<IUnknown> unk;
RETURN_IF_FAILED(classFactory.As(&unk));
RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &g_cTerminalHandoffRegistration));
_pfnHandoff = pfnHandoff;
return S_OK;
}
CATCH_RETURN()
@@ -53,15 +55,6 @@ CATCH_RETURN()
HRESULT CTerminalHandoff::s_StopListening()
{
std::unique_lock lock{ _mtx };
return s_StopListeningLocked();
}
// See s_StopListening()
HRESULT CTerminalHandoff::s_StopListeningLocked()
{
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
_pfnHandoff = nullptr;
if (g_cTerminalHandoffRegistration)
{
@@ -92,22 +85,15 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE* in, HANDLE* out, HANDLE si
{
try
{
std::unique_lock lock{ _mtx };
// s_StopListeningLocked sets _pfnHandoff to nullptr.
// localPfnHandoff is tested for nullness below.
#pragma warning(suppress : 26429) // Symbol '...' is never tested for nullness, it can be marked as not_null (f.23).
auto localPfnHandoff = _pfnHandoff;
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
// COM does not automatically clean that up for us. We must do it.
LOG_IF_FAILED(s_StopListeningLocked());
LOG_IF_FAILED(s_StopListening());
// Report an error if no one registered a handoff function before calling this.
THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
THROW_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
// Call registered handler from when we started listening.
THROW_IF_FAILED(localPfnHandoff(in, out, signal, reference, server, client, startupInfo));
THROW_IF_FAILED(_pfnHandoff(in, out, signal, reference, server, client, startupInfo));
#pragma warning(suppress : 26477)
TraceLoggingWrite(

View File

@@ -38,11 +38,11 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
#pragma endregion
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
static HRESULT s_StopListening();
static void s_setCallback(NewHandoffFunction callback) noexcept;
static HRESULT s_StartListening();
private:
static HRESULT s_StopListeningLocked();
static HRESULT s_StopListening();
};
// Disable warnings from the CoCreatableClass macro as the value it provides for

View File

@@ -780,12 +780,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void ConptyConnection::StartInboundListener()
{
THROW_IF_FAILED(CTerminalHandoff::s_StartListening(&ConptyConnection::NewHandoff));
}
static const auto init = []() noexcept {
CTerminalHandoff::s_setCallback(&ConptyConnection::NewHandoff);
return true;
}();
void ConptyConnection::StopInboundListener()
{
THROW_IF_FAILED(CTerminalHandoff::s_StopListening());
CTerminalHandoff::s_StartListening();
}
// Function Description:

View File

@@ -36,7 +36,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
WORD ShowWindow() const noexcept;
static void StartInboundListener();
static void StopInboundListener();
static winrt::event_token NewConnection(const NewConnectionHandler& handler);
static void NewConnection(const winrt::event_token& token);

View File

@@ -23,7 +23,6 @@ namespace Microsoft.Terminal.TerminalConnection
static event NewConnectionHandler NewConnection;
static void StartInboundListener();
static void StopInboundListener();
static Windows.Foundation.Collections.ValueSet CreateSettings(String cmdline,
String startingDirectory,

View File

@@ -340,7 +340,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const Core::Point pixelPosition,
const bool pointerPressedInBounds)
{
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, true);
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
// Returning true from this function indicates that the caller should do no further processing of this movement.
bool handledCompletely = false;
@@ -489,7 +489,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const Core::Point pixelPosition,
const Control::MouseButtonState buttonState)
{
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, true);
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
// Short-circuit isReadOnly check to avoid warning dialog.
//

View File

@@ -437,7 +437,8 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
}
// 0. Useful tools/vars
const auto bufferSize = _activeBuffer().GetSize();
const auto& buffer = _activeBuffer();
const auto bufferSize = buffer.GetSize();
const auto viewportHeight = _GetMutableViewport().Height();
// The patterns are stored relative to the "search area". Initially, this search area will be the viewport,
@@ -504,8 +505,18 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
};
// 1. Look for the hyperlink
til::point searchStart = dir == SearchDirection::Forward ? _selection->start : til::point{ bufferSize.Left(), _VisibleStartIndex() };
til::point searchEnd = dir == SearchDirection::Forward ? til::point{ bufferSize.RightInclusive(), _VisibleEndIndex() } : _selection->start;
til::point searchStart;
til::point searchEnd;
if (dir == SearchDirection::Forward)
{
searchStart = _selection->start;
searchEnd = til::point{ bufferSize.RightInclusive(), _VisibleEndIndex() };
}
else
{
searchStart = til::point{ bufferSize.Left(), _VisibleStartIndex() };
searchEnd = _selection->start;
}
// 1.A) Try searching the current viewport (no scrolling required)
auto resultList = _patternIntervalTree.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd));
@@ -547,27 +558,81 @@ void Terminal::SelectHyperlink(const SearchDirection dir)
searchArea = Viewport::FromDimensions(searchStart, { searchEnd.x + 1, searchEnd.y + 1 });
}
}
// 1.C) Nothing was found. Bail!
if (!result.has_value())
{
return;
}
}
// 2. Select the hyperlink
// 2. We found a hyperlink from the pattern tree. Look for embedded hyperlinks too!
// Use the result (if one was found) to narrow down the search.
if (dir == SearchDirection::Forward)
{
auto selection{ _selection.write() };
wil::hide_name _selection;
selection->start = result->first;
selection->pivot = result->first;
selection->end = result->second;
_selectionIsTargetingUrl = true;
_selectionEndpoint = SelectionEndpoint::End;
searchStart = _selection->start;
searchEnd = (result ? result->first : buffer.GetLastNonSpaceCharacter());
}
else
{
searchStart = (result ? result->second : bufferSize.Origin());
searchEnd = _selection->start;
}
// 3. Scroll to the selected area (if necessary)
_ScrollToPoint(_selection->end);
// Careful! Selection can point to RightExclusive(), which doesn't contain data!
// Clamp to be safe.
auto initialPos = dir == SearchDirection::Forward ? searchStart : searchEnd;
bufferSize.Clamp(initialPos);
auto iter = buffer.GetCellDataAt(initialPos);
while (dir == SearchDirection::Forward ? iter.Pos() < searchEnd : iter.Pos() > searchStart)
{
// Don't let us select the same hyperlink again
if (iter.Pos() < _selection->start || iter.Pos() > _selection->end)
{
if (auto attr = iter->TextAttr(); attr.IsHyperlink())
{
// Found an embedded hyperlink!
const auto hyperlinkId = attr.GetHyperlinkId();
// Expand the start to include the entire hyperlink
TextBufferCellIterator hyperlinkStartIter{ buffer, iter.Pos() };
while (hyperlinkStartIter.Pos() > searchStart && attr.IsHyperlink() && attr.GetHyperlinkId() == hyperlinkId)
{
--hyperlinkStartIter;
attr = hyperlinkStartIter->TextAttr();
}
if (hyperlinkStartIter.Pos() != bufferSize.Origin())
{
// undo a move to be inclusive
++hyperlinkStartIter;
}
// Expand the end to include the entire hyperlink
// No need to undo a move! We'll decrement in the next step anyways.
TextBufferCellIterator hyperlinkEndIter{ buffer, iter.Pos() };
attr = hyperlinkEndIter->TextAttr();
while (hyperlinkEndIter.Pos() < searchEnd && attr.IsHyperlink() && attr.GetHyperlinkId() == hyperlinkId)
{
++hyperlinkEndIter;
attr = hyperlinkEndIter->TextAttr();
}
result = { hyperlinkStartIter.Pos(), hyperlinkEndIter.Pos() };
break;
}
}
iter += dir == SearchDirection::Forward ? 1 : -1;
}
// 3. Select the hyperlink, if one exists
if (!result.has_value())
{
return;
}
auto selection{ _selection.write() };
wil::hide_name _selection;
selection->start = result->first;
selection->pivot = result->first;
selection->end = result->second;
_selectionIsTargetingUrl = true;
_selectionEndpoint = SelectionEndpoint::End;
// 4. Scroll to the selected area (if necessary)
_ScrollToPoint(selection->end);
}
Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const noexcept

View File

@@ -467,7 +467,7 @@
<FontIcon Margin="0,0,-1,-1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
FontFamily="Segoe Fluent Icons"
FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets"
FontSize="12"
Foreground="{TemplateBinding BorderBrush}"
Glyph="&#xE73D;"

View File

@@ -94,12 +94,9 @@
</ResourceDictionary>
</UserControl.Resources>
<Grid ColumnSpacing="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="ContentStackPanel"
Orientation="Horizontal"
Spacing="5">
<ContentDialog x:Name="ColorPickerDialog"
x:Uid="NullableColorPicker_ColorPickerContentDialog"
DefaultButton="Primary"
@@ -119,37 +116,61 @@
Orientation="Horizontal" />
</ContentDialog>
<ContentPresenter Grid.Column="0"
Content="{x:Bind ColorSchemeVM, Mode=OneWay}"
<ContentPresenter Content="{x:Bind ColorSchemeVM, Mode=OneWay}"
ContentTemplate="{StaticResource ColorSchemeTemplate}" />
<StackPanel Grid.Column="1"
Spacing="5">
<ToggleButton AutomationProperties.Name="{x:Bind NullColorButtonLabel}"
Click="NullColorButton_Clicked"
IsChecked="{x:Bind IsNull(CurrentColor), Mode=OneWay}">
<Grid ColumnSpacing="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Spacing="5">
<ToggleButton AutomationProperties.Name="{x:Bind NullColorButtonLabel}"
Click="NullColorButton_Clicked"
IsChecked="{x:Bind IsNull(CurrentColor), Mode=OneWay}">
<Grid ColumnSpacing="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Width="20"
Height="20"
Background="{x:Bind mtu:Converters.ColorToBrush(NullColorPreview), Mode=OneWay}"
BorderThickness="1"
CornerRadius="{ThemeResource ControlCornerRadius}" />
<Border Grid.Column="0"
Width="20"
Height="20"
Background="{x:Bind mtu:Converters.ColorToBrush(NullColorPreview), Mode=OneWay}"
BorderThickness="1"
CornerRadius="{ThemeResource ControlCornerRadius}" />
<TextBlock Grid.Column="1"
Text="{x:Bind NullColorButtonLabel}" />
</Grid>
</ToggleButton>
<TextBlock Grid.Column="1"
Text="{x:Bind NullColorButtonLabel}" />
</Grid>
</ToggleButton>
<Button x:Uid="NullableColorPicker_MoreColorsButton"
HorizontalAlignment="Stretch"
Click="MoreColors_Clicked" />
</StackPanel>
</Grid>
<Button x:Uid="NullableColorPicker_MoreColorsButton"
HorizontalAlignment="Stretch"
Click="MoreColors_Clicked" />
</StackPanel>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ContentStackPanel.Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ContentStackPanel.Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</StackPanel>
</UserControl>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Cursorfarbe aus Farbschema verwenden</value>
<value>Schemafarbe verwenden</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Vordergrundfarbe aus Farbschema verwenden</value>
<value>Schemafarbe verwenden</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Hintergrundfarbe aus Farbschema verwenden</value>
<value>Schemafarbe verwenden</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Auswahlhintergrundfarbe aus Farbschema verwenden</value>
<value>Schemafarbe verwenden</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Zeigt ein Schild in der Titelleiste an, wenn Windows Terminal als Administrator ausgeführt wird.</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Registerkarten im Vollbildmodus anzeigen</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Wenn diese Option aktiviert ist, wird die Registerkartenleiste angezeigt, wenn sich die App im Vollbildmodus befindet.</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Pfadübersetzung</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2033,19 +2033,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use cursor color from color scheme</value>
<value>Use scheme color</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use foreground color from color scheme</value>
<value>Use scheme color</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use background color from color scheme</value>
<value>Use scheme color</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Use selection background color from color scheme</value>
<value>Use scheme color</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar el color del cursor de la combinación de colores</value>
<value>Usar color de la combinación</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar el color de primer plano de la combinación de colores</value>
<value>Usar color de la combinación</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar el color de fondo de la combinación de colores</value>
<value>Usar color de la combinación</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar el color de fondo de selección de la combinación de colores</value>
<value>Usar color de la combinación</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Mostrar un escudo en la barra de título cuando Terminal Windows se ejecute como administrador</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Mostrar las pestañas en pantalla completa</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Cuando está habilitada, la barra de pestañas estará visible cuando la aplicación esté en pantalla completa.</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Traducción de ruta de acceso</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Utilisez la couleur du curseur à partir du jeu de couleurs</value>
<value>Utiliser la couleur du jeu</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Utilisez la couleur de premier plan du jeu de couleurs</value>
<value>Utiliser la couleur du jeu</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Utilisez la couleur d'arrière-plan du jeu de couleurs</value>
<value>Utiliser la couleur du jeu</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Utilisez la couleur d'arrière-plan de sélection à partir du jeu de couleurs</value>
<value>Utiliser la couleur du jeu</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Afficher un bouclier dans la barre de titre lorsque Terminal Windows sexécute en tant quadministrateur</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Afficher les onglets en mode plein écran</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Lorsque cette option est activée, la barre donglets est visible lorsque lapplication est en plein écran.</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Traduction du chemin daccès</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usa il colore del cursore dalla combinazione colori</value>
<value>Usa colore combinazione</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usa il colore primo piano dalla combinazione colori</value>
<value>Usa colore combinazione</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usa il colore di sfondo della combinazione colori</value>
<value>Usa colore combinazione</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usa il colore di sfondo della selezione dalla combinazione colori</value>
<value>Usa colore combinazione</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Visualizza uno scudo nella barra del titolo quando Terminale Windows viene eseguito come amministratore</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Mostra le schede a schermo intero</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Se questa opzione è abilitata, la barra delle schede sarà visibile quando l'app è a schermo intero.</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Traduzione percorso</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>配色からカーソルの色を使用する</value>
<value>配色を使用</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>配色から前景色を使用する</value>
<value>配色を使用</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>配色から背景色を使用する</value>
<value>配色を使用</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>配色から選択範囲の背景色を使用する</value>
<value>配色を使用</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Windows ターミナルが管理者として実行されているときにタイトル バーにシールドを表示する</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>全画面表示でタブを表示</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>有効にすると、アプリが全画面表示のときにタブ バーが表示されます。</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>パスの変換</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>구성표에서 커서 색 사용</value>
<value>구성표 색 사용</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>구성표에서 전경색 사용</value>
<value>구성표 색 사용</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>구성표의 배경색 사용</value>
<value>구성표 색 사용</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>구성표에서 선택 배경색 사용</value>
<value>구성표 색 사용</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Windows 터미널 관리자 권한으로 실행 중일 때 제목 표시줄에 실드 표시</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>전체 화면으로 탭 표시</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>사용하도록 설정하면 앱이 전체 화면일 때 탭 표시줄이 표시됩니다.</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>경로 변환</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar a cor do cursor do esquema de cores</value>
<value>Usar cor do esquema</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar a cor de primeiro plano do esquema de cores</value>
<value>Usar cor do esquema</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar a cor da tela de fundo do esquema de cores</value>
<value>Usar cor do esquema</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Usar a cor da tela de fundo de seleção do esquema de cores</value>
<value>Usar cor do esquema</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Exibir um escudo na barra de título quando o Terminal do Windows estiver sendo executado como Administrador</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Mostrar as guias em tela inteira</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Quando habilitada, a barra de guias ficará visível quando o aplicativo estiver em tela inteira.</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Tradução de caminho</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2033,19 +2033,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ùŝë ćυŗšθг çσľōř ƒŗōm ĉöŀøя śĉнзмé !!! !!! !!! !</value>
<value>Ùŝë ŝčĥзмё çσľōř !!! !</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ŭŝě ƒθгęĝѓóϋňď ćǿłõѓ ƒŕõм сöℓòя şçĥέмё !!! !!! !!! !!</value>
<value>Ŭŝě şςђęmε ¢оłöř !!! !</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ůśĕ вäćкģŗòŭиð ćôℓοѓ ƒŕǿm čòℓôѓ śςĥëmē !!! !!! !!! !!</value>
<value>Ůśĕ śĉħемë ¢ōĺóř !!! !</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ůŝë śęŀ℮çťïòл ьαćкğŕόµńđ çøłôř ƒŗоm ¢οłõг ş¢нέмё !!! !!! !!! !!! !!</value>
<value>Ůŝë śĉћmĕ çòĺőг !!! !</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2280,6 +2280,14 @@
<value>Đįŝрļãу ã ŝħĭěļð ίπ ŧђê ţîťłë ъãѓ щћэπ Ẅīήđθщş Ţĕřмïńāľ ίѕ ŕůπʼnïηģ åš Àδмιήίŝтяàтοґ !!! !!! !!! !!! !!! !!! !!! !!! </value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Şĥбω ťãьŝ іή ƒūļℓ šĉґééл !!! !!! !</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Шнèŋ ěπǻьłéđ, тнĕ ŧªв вář ẁϊℓł ь℮ νìşίьĺę шћеň τћé άрр íś ƒûĺĺ şčŗёеņ. !!! !!! !!! !!! !!! !!! !!!</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Ρǻţħ τґãñşĺαŧîσй !!! !</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2033,19 +2033,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ùŝë ćυŗšθг çσľōř ƒŗōm ĉöŀøя śĉнзмé !!! !!! !!! !</value>
<value>Ùŝë ŝčĥзмё çσľōř !!! !</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ŭŝě ƒθгęĝѓóϋňď ćǿłõѓ ƒŕõм сöℓòя şçĥέмё !!! !!! !!! !!</value>
<value>Ŭŝě şςђęmε ¢оłöř !!! !</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ůśĕ вäćкģŗòŭиð ćôℓοѓ ƒŕǿm čòℓôѓ śςĥëmē !!! !!! !!! !!</value>
<value>Ůśĕ śĉħемë ¢ōĺóř !!! !</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ůŝë śęŀ℮çťïòл ьαćкğŕόµńđ çøłôř ƒŗоm ¢οłõг ş¢нέмё !!! !!! !!! !!! !!</value>
<value>Ůŝë śĉћmĕ çòĺőг !!! !</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2280,6 +2280,14 @@
<value>Đįŝрļãу ã ŝħĭěļð ίπ ŧђê ţîťłë ъãѓ щћэπ Ẅīήđθщş Ţĕřмïńāľ ίѕ ŕůπʼnïηģ åš Àδмιήίŝтяàтοґ !!! !!! !!! !!! !!! !!! !!! !!! </value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Şĥбω ťãьŝ іή ƒūļℓ šĉґééл !!! !!! !</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Шнèŋ ěπǻьłéđ, тнĕ ŧªв вář ẁϊℓł ь℮ νìşίьĺę шћеň τћé άрр íś ƒûĺĺ şčŗёеņ. !!! !!! !!! !!! !!! !!! !!!</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Ρǻţħ τґãñşĺαŧîσй !!! !</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2033,19 +2033,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ùŝë ćυŗšθг çσľōř ƒŗōm ĉöŀøя śĉнзмé !!! !!! !!! !</value>
<value>Ùŝë ŝčĥзмё çσľōř !!! !</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ŭŝě ƒθгęĝѓóϋňď ćǿłõѓ ƒŕõм сöℓòя şçĥέмё !!! !!! !!! !!</value>
<value>Ŭŝě şςђęmε ¢оłöř !!! !</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ůśĕ вäćкģŗòŭиð ćôℓοѓ ƒŕǿm čòℓôѓ śςĥëmē !!! !!! !!! !!</value>
<value>Ůśĕ śĉħемë ¢ōĺóř !!! !</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Ůŝë śęŀ℮çťïòл ьαćкğŕόµńđ çøłôř ƒŗоm ¢οłõг ş¢нέмё !!! !!! !!! !!! !!</value>
<value>Ůŝë śĉћmĕ çòĺőг !!! !</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2280,6 +2280,14 @@
<value>Đįŝрļãу ã ŝħĭěļð ίπ ŧђê ţîťłë ъãѓ щћэπ Ẅīήđθщş Ţĕřмïńāľ ίѕ ŕůπʼnïηģ åš Àδмιήίŝтяàтοґ !!! !!! !!! !!! !!! !!! !!! !!! </value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Şĥбω ťãьŝ іή ƒūļℓ šĉґééл !!! !!! !</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Шнèŋ ěπǻьłéđ, тнĕ ŧªв вář ẁϊℓł ь℮ νìşίьĺę шћеň τћé άрр íś ƒûĺĺ şčŗёеņ. !!! !!! !!! !!! !!! !!! !!!</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Ρǻţħ τґãñşĺαŧîσй !!! !</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Использовать цвет курсора из цветовой схемы</value>
<value>Использовать цвет схемы</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Использовать цвет переднего плана из цветовой схемы</value>
<value>Использовать цвет схемы</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Использовать цвет фона из цветовой схемы</value>
<value>Использовать цвет схемы</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>Использовать выбранный цвет фона из цветовой схемы</value>
<value>Использовать цвет схемы</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>Отображать экран в заголовке, когда Терминал Windows от имени администратора</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>Показывать вкладки в полноэкранном режиме</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>Если этот параметр включен, панель вкладок будет отображаться, когда приложение будет полноэкранным.</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Преобразование пути</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用配色方案中的光标色</value>
<value>使用配色方案色</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用配色方案中的前景色</value>
<value>使用配色方案色</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用配色方案中的背景色</value>
<value>使用配色方案色</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用配色方案中的选择背景色</value>
<value>使用配色方案色</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>当 Windows 终端以管理员身份运行时,在标题栏中显示一个盾牌图标</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>全屏显示选项卡</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>启用后,当应用处于全屏状态时,选项卡栏将可见。</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>路径转换</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -2029,19 +2029,19 @@
<comment>Text label for secondary button the color picker content dialog. When clicked, the operation of selecting a color is cancelled by the user.</comment>
</data>
<data name="Profile_CursorColor_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用色彩配置的游標色彩</value>
<value>使用配置色彩</value>
<comment>Label for a button directing the user to use the cursor color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Foreground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用色彩配置的前景色彩</value>
<value>使用配置色彩</value>
<comment>Label for a button directing the user to use the foreground color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_Background_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用色彩配置的背景色彩</value>
<value>使用配置色彩</value>
<comment>Label for a button directing the user to use the background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_SelectionBackground_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
<value>使用色彩配置的選取項目背景色彩</value>
<value>使用配置色彩</value>
<comment>Label for a button directing the user to use the selection background color defined in the terminal's current color scheme.</comment>
</data>
<data name="Profile_IconTypeNone" xml:space="preserve">
@@ -2276,6 +2276,14 @@
<value>當 Windows 終端機 以系統管理員身分執行時,在標題欄中顯示遮罩</value>
<comment>Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin"</comment>
</data>
<data name="Globals_ShowTabsFullscreen.Header" xml:space="preserve">
<value>以全螢幕顯示索引標籤</value>
<comment>Header for a control to toggle if the app should show the tabs when in full screen state.</comment>
</data>
<data name="Globals_ShowTabsFullscreen.HelpText" xml:space="preserve">
<value>啟用時,當應用程式使用全螢幕時,將會顯示索引標籤。</value>
<comment>A description for what the "show tabs in full screen" setting does.</comment>
</data>
<data name="Profile_PathTranslationStyle.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>路徑翻譯</value>
<comment>Name for a control to select how file and directory paths are translated.</comment>

View File

@@ -354,7 +354,7 @@
<value>タブのタイトルをリセット</value>
</data>
<data name="OpenTabRenamerCommandKey" xml:space="preserve">
<value>[名前の変更] タブ</value>
<value>タブ名を変更</value>
</data>
<data name="ResizePaneCommandKey" xml:space="preserve">
<value>ウィンドウのサイズの変更する</value>

View File

@@ -81,6 +81,29 @@
// - "foreground"
// - "background"
// - "cursorColor"
{
"name": "Ottosson",
"background": "#000000",
"foreground": "#bebebe",
"cursorColor": "#ffffff",
"selectionBackground": "#92a4fd",
"black": "#000000",
"red": "#be2c21",
"green": "#3fae3a",
"yellow": "#be9a4a",
"blue": "#204dbe",
"purple": "#bb54be",
"cyan": "#00a7b2",
"white": "#bebebe",
"brightBlack": "#808080",
"brightRed": "#ff3e30",
"brightGreen": "#58ea51",
"brightYellow": "#ffc944",
"brightBlue": "#2f6aff",
"brightPurple": "#fc74ff",
"brightCyan": "#00e1f0",
"brightWhite": "#ffffff"
},
{
"name": "Campbell",
"foreground": "#CCCCCC",

View File

@@ -95,6 +95,12 @@ void IslandWindow::Close()
if (_source)
{
// BODGY
// WinUI will strongly hold onto the first DesktopWindowXamlSource that is created.
// If we don't manually set the Content() to null first, closing that first window
// will leak all of its contents permanently.
_source.Content(nullptr);
_source.Close();
_source = nullptr;
}

View File

@@ -246,6 +246,8 @@ void WindowEmperor::CreateNewWindow(winrt::TerminalApp::WindowRequestedArgs args
auto host = std::make_shared<AppHost>(this, _app.Logic(), std::move(args));
host->Initialize();
_windowCount += 1;
_windows.emplace_back(std::move(host));
}
@@ -735,9 +737,14 @@ void WindowEmperor::_createMessageWindow(const wchar_t* className)
StringCchCopy(_notificationIcon.szTip, ARRAYSIZE(_notificationIcon.szTip), appNameLoc.c_str());
}
// Posts a WM_QUIT as soon as we have no reason to exist anymore.
// That basically means no windows and no message boxes.
void WindowEmperor::_postQuitMessageIfNeeded() const
{
if (_messageBoxCount <= 0 && _windows.empty() && !_app.Logic().Settings().GlobalSettings().AllowHeadless())
if (
_messageBoxCount <= 0 &&
_windowCount <= 0 &&
!_app.Logic().Settings().GlobalSettings().AllowHeadless())
{
PostQuitMessage(0);
}
@@ -771,20 +778,37 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
{
case WM_CLOSE_TERMINAL_WINDOW:
{
const auto host = reinterpret_cast<AppHost*>(lParam);
auto it = _windows.begin();
const auto end = _windows.end();
const auto globalSettings = _app.Logic().Settings().GlobalSettings();
// Keep the last window in the array so that we can persist it on exit.
// We check for AllowHeadless(), as that being true prevents us from ever quitting in the first place.
// (= If we avoided closing the last window you wouldn't be able to reach a headless state.)
const auto shouldKeepWindow =
_windows.size() == 1 &&
globalSettings.ShouldUsePersistedLayout() &&
!globalSettings.AllowHeadless();
for (; it != end; ++it)
if (!shouldKeepWindow)
{
if (host == it->get())
// Did the window counter get out of sync? It shouldn't.
assert(_windowCount == gsl::narrow_cast<int32_t>(_windows.size()));
const auto host = reinterpret_cast<AppHost*>(lParam);
auto it = _windows.begin();
const auto end = _windows.end();
for (; it != end; ++it)
{
host->Close();
_windows.erase(it);
break;
if (host == it->get())
{
host->Close();
_windows.erase(it);
break;
}
}
}
// Counterpart specific to CreateNewWindow().
_windowCount -= 1;
_postQuitMessageIfNeeded();
return 0;
}

View File

@@ -55,6 +55,7 @@ private:
safe_void_coroutine _dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args);
LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept;
void _createMessageWindow(const wchar_t* className);
bool _shouldSkipClosingWindows() const;
void _postQuitMessageIfNeeded() const;
safe_void_coroutine _showMessageBox(winrt::hstring message, bool error);
void _notificationAreaMenuRequested(WPARAM wParam);
@@ -77,6 +78,7 @@ private:
bool _forcePersistence = false;
bool _needsPersistenceCleanup = false;
std::optional<bool> _currentSystemThemeIsDark;
int32_t _windowCount = 0;
int32_t _messageBoxCount = 0;
#ifdef NDEBUG

View File

@@ -79,7 +79,11 @@ using namespace Microsoft::Console::Interactivity;
const HANDLE OutHandle,
_In_opt_ const HANDLE SignalHandle)
{
FAIL_FAST_IF_MSG(_initialized, "Someone attempted to double-_Initialize VtIo");
if (_state != State::Uninitialized)
{
assert(false); // Don't call initialize twice.
return E_UNEXPECTED;
}
_hInput.reset(InHandle);
_hOutput.reset(OutHandle);
@@ -95,47 +99,33 @@ using namespace Microsoft::Console::Interactivity;
}
}
// - Create and start the signal thread. The signal thread can be created
// independent of the i/o threads, and doesn't require a client first
// attaching to the console. We need to create it first and foremost,
// because it's possible that a terminal application could
// CreatePseudoConsole, then ClosePseudoConsole without ever attaching a
// client. Should that happen, we still need to exit.
if (IsValidHandle(_hSignal.get()))
{
try
{
_pPtySignalInputThread = std::make_unique<PtySignalInputThread>(std::move(_hSignal));
// Start it if it was successfully created.
RETURN_IF_FAILED(_pPtySignalInputThread->Start());
}
CATCH_RETURN();
}
// The only way we're initialized is if the args said we're in conpty mode.
// If the args say so, then at least one of in, out, or signal was specified
_initialized = true;
return S_OK;
}
// Method Description:
// - Create the VtEngine and the VtInputThread for this console.
// MUST BE DONE AFTER CONSOLE IS INITIALIZED, to make sure we've gotten the
// buffer size from the attached client application.
// Arguments:
// - <none>
// Return Value:
// S_OK if we initialized successfully,
// S_FALSE if VtIo hasn't been initialized (or we're not in conpty mode)
// otherwise an appropriate HRESULT indicating failure.
[[nodiscard]] HRESULT VtIo::CreateIoHandlers() noexcept
{
if (!_initialized)
{
return S_FALSE;
}
// SetWindowVisibility uses the console lock to protect access to _pVtRenderEngine.
assert(ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked());
try
{
if (IsValidHandle(_hInput.get()))
{
_pVtInputThread = std::make_unique<VtInputThread>(std::move(_hInput), _lookingForCursorPosition);
}
}
CATCH_RETURN();
_state = State::Initialized;
return S_OK;
}
bool VtIo::IsUsingVt() const
{
return _initialized;
return _state != State::Uninitialized;
}
// Routine Description:
@@ -151,50 +141,64 @@ bool VtIo::IsUsingVt() const
[[nodiscard]] HRESULT VtIo::StartIfNeeded()
{
// If we haven't been set up, do nothing (because there's nothing to start)
if (!_initialized)
if (_state != State::Initialized)
{
return S_FALSE;
}
_state = State::Starting;
// SetWindowVisibility uses the console lock to protect access to _pVtRenderEngine.
assert(ServiceLocator::LocateGlobals().getConsoleInformation().IsConsoleLocked());
try
{
if (IsValidHandle(_hInput.get()))
{
_pVtInputThread = std::make_unique<VtInputThread>(std::move(_hInput), _lookingForCursorPosition);
}
}
CATCH_RETURN();
if (_pVtInputThread)
{
LOG_IF_FAILED(_pVtInputThread->Start());
}
{
Writer writer{ this };
// MSFT: 15813316
// If the terminal application wants us to inherit the cursor position,
// we're going to emit a VT sequence to ask for the cursor position.
// If we get a response, the InteractDispatch will call SetCursorPosition,
// which will call to our VtIo::SetCursorPosition method.
//
// By sending the request before sending the DA1 one, we can simply
// wait for the DA1 response below and effectively wait for both.
if (_lookingForCursorPosition)
{
writer.WriteUTF8("\x1b[6n"); // Cursor Position Report (DSR CPR)
Writer writer{ this };
// MSFT: 15813316
// If the terminal application wants us to inherit the cursor position,
// we're going to emit a VT sequence to ask for the cursor position.
// If we get a response, the InteractDispatch will call SetCursorPosition,
// which will call to our VtIo::SetCursorPosition method.
//
// By sending the request before sending the DA1 one, we can simply
// wait for the DA1 response below and effectively wait for both.
if (_lookingForCursorPosition)
{
writer.WriteUTF8("\x1b[6n"); // Cursor Position Report (DSR CPR)
}
// GH#4999 - Send a sequence to the connected terminal to request
// win32-input-mode from them. This will enable the connected terminal to
// send us full INPUT_RECORDs as input. If the terminal doesn't understand
// this sequence, it'll just ignore it.
writer.WriteUTF8(
"\x1b[c" // DA1 Report (Primary Device Attributes)
"\x1b[?1004h" // Focus Event Mode
"\x1b[?9001h" // Win32 Input Mode
);
writer.Submit();
}
// GH#4999 - Send a sequence to the connected terminal to request
// win32-input-mode from them. This will enable the connected terminal to
// send us full INPUT_RECORDs as input. If the terminal doesn't understand
// this sequence, it'll just ignore it.
writer.WriteUTF8(
"\x1b[c" // DA1 Report (Primary Device Attributes)
"\x1b[?1004h" // Focus Event Mode
"\x1b[?9001h" // Win32 Input Mode
);
writer.Submit();
}
{
// Allow the input thread to momentarily gain the console lock.
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto suspension = gci.SuspendLock();
_deviceAttributes = _pVtInputThread->WaitUntilDA1(3000);
{
// Allow the input thread to momentarily gain the console lock.
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto suspension = gci.SuspendLock();
_deviceAttributes = _pVtInputThread->WaitUntilDA1(3000);
}
}
if (_pPtySignalInputThread)
@@ -208,6 +212,17 @@ bool VtIo::IsUsingVt() const
_pPtySignalInputThread->ConnectConsole();
}
if (_state != State::Starting)
{
// Here's where we _could_ call CloseConsoleProcessState(), but this function
// only gets called once when the first client connects and CONSOLE_INITIALIZED
// is not set yet. The process list may already contain that first client,
// but since it hasn't finished connecting yet, it won't react to a CTRL_CLOSE_EVENT.
// Instead, we return an error here which will abort the connection setup.
return E_FAIL;
}
_state = State::Running;
return S_OK;
}
@@ -244,47 +259,21 @@ void VtIo::CreatePseudoWindow()
}
}
// Method Description:
// - Create and start the signal thread. The signal thread can be created
// independent of the i/o threads, and doesn't require a client first
// attaching to the console. We need to create it first and foremost,
// because it's possible that a terminal application could
// CreatePseudoConsole, then ClosePseudoConsole without ever attaching a
// client. Should that happen, we still need to exit.
// Arguments:
// - <none>
// Return Value:
// - S_FALSE if we're not in VtIo mode,
// S_OK if we succeeded,
// otherwise an appropriate HRESULT indicating failure.
[[nodiscard]] HRESULT VtIo::CreateAndStartSignalThread() noexcept
{
if (!_initialized)
{
return S_FALSE;
}
// If we were passed a signal handle, try to open it and make a signal reading thread.
if (IsValidHandle(_hSignal.get()))
{
try
{
_pPtySignalInputThread = std::make_unique<PtySignalInputThread>(std::move(_hSignal));
// Start it if it was successfully created.
RETURN_IF_FAILED(_pPtySignalInputThread->Start());
}
CATCH_RETURN();
}
return S_OK;
}
void VtIo::SendCloseEvent()
{
LockConsole();
const auto unlock = wil::scope_exit([] { UnlockConsole(); });
// If we're still in the process of starting up, and we're asked to shut down
// (broken pipe), `VtIo::StartIfNeeded()` will handle the cleanup for us.
// This can happen during the call to `WaitUntilDA1`, because we relinquish
// ownership of the console lock.
if (_state == State::Starting)
{
_state = State::StartupFailed;
return;
}
// This function is called when the ConPTY signal pipe is closed (PtySignalInputThread) and when the input
// pipe is closed (VtIo). Usually these two happen at about the same time. This if condition is a bit of
// a premature optimization and prevents us from sending out a CTRL_CLOSE_EVENT right after another.

View File

@@ -57,8 +57,6 @@ namespace Microsoft::Console::VirtualTerminal
static wchar_t SanitizeUCS2(wchar_t ch);
[[nodiscard]] HRESULT Initialize(const ConsoleArguments* const pArgs);
[[nodiscard]] HRESULT CreateAndStartSignalThread() noexcept;
[[nodiscard]] HRESULT CreateIoHandlers() noexcept;
bool IsUsingVt() const;
[[nodiscard]] HRESULT StartIfNeeded();
@@ -69,6 +67,15 @@ namespace Microsoft::Console::VirtualTerminal
void CreatePseudoWindow();
private:
enum class State : uint8_t
{
Uninitialized,
Initialized,
Starting,
StartupFailed,
Running,
};
[[nodiscard]] HRESULT _Initialize(const HANDLE InHandle, const HANDLE OutHandle, _In_opt_ const HANDLE SignalHandle);
void _uncork();
@@ -77,7 +84,7 @@ namespace Microsoft::Console::VirtualTerminal
// After CreateIoHandlers is called, these will be invalid.
wil::unique_hfile _hInput;
wil::unique_hfile _hOutput;
// After CreateAndStartSignalThread is called, this will be invalid.
// After Initialize is called, this will be invalid.
wil::unique_hfile _hSignal;
std::unique_ptr<Microsoft::Console::VtInputThread> _pVtInputThread;
@@ -96,7 +103,7 @@ namespace Microsoft::Console::VirtualTerminal
bool _writerRestoreCursor = false;
bool _writerTainted = false;
bool _initialized = false;
State _state = State::Uninitialized;
bool _lookingForCursorPosition = false;
bool _closeEventSent = false;
int _corked = 0;

View File

@@ -381,7 +381,6 @@ HRESULT ConsoleCreateIoThread(_In_ HANDLE Server,
// can start, so they're started below, in ConsoleAllocateConsole
auto& gci = g.getConsoleInformation();
RETURN_IF_FAILED(gci.GetVtIo()->Initialize(args));
RETURN_IF_FAILED(gci.GetVtIo()->CreateAndStartSignalThread());
return S_OK;
}
@@ -945,27 +944,11 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand,
// We'll need the size of the screen buffer in the vt i/o initialization
if (SUCCEEDED_NTSTATUS(Status))
{
auto hr = gci.GetVtIo()->CreateIoHandlers();
if (hr == S_FALSE)
{
// We're not in VT I/O mode, this is fine.
}
else if (SUCCEEDED(hr))
{
// Actually start the VT I/O threads
hr = gci.GetVtIo()->StartIfNeeded();
// Don't convert S_FALSE to an NTSTATUS - the equivalent NTSTATUS
// is treated as an error
if (hr != S_FALSE)
{
Status = NTSTATUS_FROM_HRESULT(hr);
}
else
{
Status = ERROR_SUCCESS;
}
}
else
// Actually start the VT I/O threads
auto hr = gci.GetVtIo()->StartIfNeeded();
// Don't convert S_FALSE to an NTSTATUS - the equivalent NTSTATUS
// is treated as an error
if (FAILED(hr))
{
Status = NTSTATUS_FROM_HRESULT(hr);
}
@@ -1008,7 +991,7 @@ DWORD WINAPI ConsoleIoThread(LPVOID lpParameter)
{
if (ReplyMsg != nullptr)
{
LOG_IF_FAILED(ReplyMsg->ReleaseMessageBuffers());
ReplyMsg->ReleaseMessageBuffers();
}
// TODO: 9115192 correct mixed NTSTATUS/HRESULT

View File

@@ -180,12 +180,8 @@ CATCH_RETURN();
// (if any) to the message.
// Arguments:
// - <none>
// Return Value:
// - HRESULT indicating if the payload was successfully written if applicable.
[[nodiscard]] HRESULT _CONSOLE_API_MSG::ReleaseMessageBuffers()
void _CONSOLE_API_MSG::ReleaseMessageBuffers()
{
auto hr = S_OK;
if (State.InputBuffer != nullptr)
{
_inputBuffer.clear();
@@ -203,15 +199,15 @@ CATCH_RETURN();
IoOperation.Buffer.Data = State.OutputBuffer;
IoOperation.Buffer.Size = (ULONG)Complete.IoStatus.Information;
LOG_IF_FAILED(_pDeviceComm->WriteOutput(&IoOperation));
// This call fails when the server pipe is closed on us,
// which results in log spam in practice.
std::ignore = _pDeviceComm->WriteOutput(&IoOperation);
}
_outputBuffer.clear();
State.OutputBuffer = nullptr;
State.OutputBufferSize = 0;
}
return hr;
}
void _CONSOLE_API_MSG::SetReplyStatus(const NTSTATUS Status)

View File

@@ -42,7 +42,7 @@ typedef struct _CONSOLE_API_MSG
[[nodiscard]] HRESULT GetOutputBuffer(_Outptr_result_bytebuffer_(*pcbSize) void** const ppvBuffer, _Out_ ULONG* const pcbSize);
[[nodiscard]] HRESULT GetInputBuffer(_Outptr_result_bytebuffer_(*pcbSize) void** const ppvBuffer, _Out_ ULONG* const pcbSize);
[[nodiscard]] HRESULT ReleaseMessageBuffers();
void ReleaseMessageBuffers();
void SetReplyStatus(const NTSTATUS Status);
void SetReplyInformation(const ULONG_PTR pInformation);

View File

@@ -137,14 +137,15 @@ ConDrvDeviceComm::~ConDrvDeviceComm() = default;
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
// Written is unused but cannot be nullptr because we aren't using overlapped.
DWORD cbWritten = 0;
RETURN_IF_WIN32_BOOL_FALSE(DeviceIoControl(_Server.get(),
dwIoControlCode,
pInBuffer,
cbInBufferSize,
pOutBuffer,
cbOutBufferSize,
&cbWritten,
nullptr));
RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(DeviceIoControl(
_Server.get(),
dwIoControlCode,
pInBuffer,
cbInBufferSize,
pOutBuffer,
cbOutBufferSize,
&cbWritten,
nullptr));
return S_OK;
}

View File

@@ -223,9 +223,11 @@ bool ConsoleWaitBlock::Notify(const WaitTerminationReason TerminationReason)
a->NumBytes = gsl::narrow<ULONG>(NumBytes);
}
LOG_IF_FAILED(_WaitReplyMessage.ReleaseMessageBuffers());
_WaitReplyMessage.ReleaseMessageBuffers();
LOG_IF_FAILED(Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().pDeviceComm->CompleteIo(&_WaitReplyMessage.Complete));
// This call fails when the server pipe is closed on us,
// which results in log spam in practice.
std::ignore = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().pDeviceComm->CompleteIo(&_WaitReplyMessage.Complete);
fRetVal = true;
}

View File

@@ -471,15 +471,22 @@ bool InputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParameter
// We catch it here and store the information for later retrieval.
if (_deviceAttributes.load(std::memory_order_relaxed) == 0)
{
til::enumset<DeviceAttribute, uint64_t> attributes;
til::enumset<DeviceAttribute, uint64_t> attributes{ DeviceAttribute::__some__ };
// The first parameter denotes the conformance level.
if (parameters.at(0).value() >= 61)
const auto len = parameters.size();
if (len >= 2 && parameters.at(0).value() >= 61)
{
parameters.subspan(1).for_each([&](auto p) {
attributes.set(static_cast<DeviceAttribute>(p));
return true;
});
// NOTE: VTParameters::for_each will replace empty spans with a single default value.
// This means we could not distinguish between no parameters and a single default parameter.
for (size_t i = 1; i < len; i++)
{
const auto value = parameters.at(i).value();
if (value > 0 && value < 64)
{
attributes.set(static_cast<DeviceAttribute>(value));
}
}
}
_deviceAttributes.fetch_or(attributes.bits(), std::memory_order_relaxed);

View File

@@ -51,6 +51,10 @@ namespace Microsoft::Console::VirtualTerminal
enum class DeviceAttribute : uint64_t
{
// Special value to indicate that InputStateMachineEngine::_deviceAttributes has been set.
// 0 in this case means 1<<0 == 1, which in turn means that _deviceAttributes is non-zero.
__some__ = 0,
Columns132 = 1,
PrinterPort = 2,
Sixel = 4,