2019-05-03 00:29:04 +02:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "Terminal.hpp"
|
2019-07-31 01:28:28 +02:00
|
|
|
#include "../src/inc/unicode.hpp"
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
using namespace Microsoft::Terminal::Core;
|
|
|
|
using namespace Microsoft::Console::Types;
|
|
|
|
using namespace Microsoft::Console::VirtualTerminal;
|
|
|
|
|
|
|
|
// Print puts the text in the buffer and moves the cursor
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::PrintString(std::wstring_view stringView) noexcept
|
|
|
|
try
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
_WriteBuffer(stringView);
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::ExecuteChar(wchar_t wch) noexcept
|
|
|
|
try
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
2020-01-03 19:44:27 +01:00
|
|
|
_WriteBuffer({ &wch, 1 });
|
2019-05-03 00:29:04 +02:00
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-05-03 00:29:04 +02:00
|
|
|
|
Refactor TerminalDispatch (graphics) to match AdaptDispatch (#6728)
This is essentially a rewrite of the
`TerminalDispatch::SetGraphicsRendition` method, bringing it into closer
alignment with the `AdaptDispatch` implementation, simplifying the
`ITerminalApi` interface, and making the code easier to extend. It adds
support for a number of attributes which weren't previously implemented.
REFERENCES
* This is a mirror of the `AdaptDispatch` refactoring in PR #5758.
* The closer alignment with `AdaptDispatch` is a small step towards
solving issue #3849.
* The newly supported attributes should help a little with issues #5461
(italics) and #6205 (strike-through).
DETAILS
I've literally copied and pasted the `SetGraphicsRendition`
implementation from `AdaptDispatch` into `TerminalDispatch`, with only
few minor changes:
* The `SetTextAttribute` and `GetTextAttribute` calls are slightly
different in the `TerminalDispatch` version, since they don't return a
pointless `success` value, and in the case of the getter, the
`TextAttribute` is returned directly instead of by reference.
Ultimately I'd like to move the `AdaptDispatch` code towards that way
of doing things too, but I'd like to deal with that later as part of a
wider refactoring of the `ConGetSet` interface.
* The `SetIndexedForeground256` and `SetIndexedBackground256` calls
required the color indices to be remapped in the `AdaptDispatch`
implementation, because the conhost color table is in a different
order to the XTerm standard. `TerminalDispatch` doesn't have that
problem, so doesn't require the mapping.
* The index color constants used in the 16-color `SetIndexedForeground`
and `SetIndexedBackground` calls are also slightly different for the
same reason.
VALIDATION
I cherry-picked this code on top of the #6506 and #6698 PRs, since
that's only way to really get the different color formats passed-through
to the terminal. I then ran a bunch of manual tests with various color
coverage scripts that I have, and confirmed that all the different color
formats were being rendered as expected.
Closes #6725
2020-07-01 20:13:42 +02:00
|
|
|
TextAttribute Terminal::GetTextAttributes() const noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
Refactor TerminalDispatch (graphics) to match AdaptDispatch (#6728)
This is essentially a rewrite of the
`TerminalDispatch::SetGraphicsRendition` method, bringing it into closer
alignment with the `AdaptDispatch` implementation, simplifying the
`ITerminalApi` interface, and making the code easier to extend. It adds
support for a number of attributes which weren't previously implemented.
REFERENCES
* This is a mirror of the `AdaptDispatch` refactoring in PR #5758.
* The closer alignment with `AdaptDispatch` is a small step towards
solving issue #3849.
* The newly supported attributes should help a little with issues #5461
(italics) and #6205 (strike-through).
DETAILS
I've literally copied and pasted the `SetGraphicsRendition`
implementation from `AdaptDispatch` into `TerminalDispatch`, with only
few minor changes:
* The `SetTextAttribute` and `GetTextAttribute` calls are slightly
different in the `TerminalDispatch` version, since they don't return a
pointless `success` value, and in the case of the getter, the
`TextAttribute` is returned directly instead of by reference.
Ultimately I'd like to move the `AdaptDispatch` code towards that way
of doing things too, but I'd like to deal with that later as part of a
wider refactoring of the `ConGetSet` interface.
* The `SetIndexedForeground256` and `SetIndexedBackground256` calls
required the color indices to be remapped in the `AdaptDispatch`
implementation, because the conhost color table is in a different
order to the XTerm standard. `TerminalDispatch` doesn't have that
problem, so doesn't require the mapping.
* The index color constants used in the 16-color `SetIndexedForeground`
and `SetIndexedBackground` calls are also slightly different for the
same reason.
VALIDATION
I cherry-picked this code on top of the #6506 and #6698 PRs, since
that's only way to really get the different color formats passed-through
to the terminal. I then ran a bunch of manual tests with various color
coverage scripts that I have, and confirmed that all the different color
formats were being rendered as expected.
Closes #6725
2020-07-01 20:13:42 +02:00
|
|
|
return _buffer->GetCurrentAttributes();
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
|
|
|
|
Refactor TerminalDispatch (graphics) to match AdaptDispatch (#6728)
This is essentially a rewrite of the
`TerminalDispatch::SetGraphicsRendition` method, bringing it into closer
alignment with the `AdaptDispatch` implementation, simplifying the
`ITerminalApi` interface, and making the code easier to extend. It adds
support for a number of attributes which weren't previously implemented.
REFERENCES
* This is a mirror of the `AdaptDispatch` refactoring in PR #5758.
* The closer alignment with `AdaptDispatch` is a small step towards
solving issue #3849.
* The newly supported attributes should help a little with issues #5461
(italics) and #6205 (strike-through).
DETAILS
I've literally copied and pasted the `SetGraphicsRendition`
implementation from `AdaptDispatch` into `TerminalDispatch`, with only
few minor changes:
* The `SetTextAttribute` and `GetTextAttribute` calls are slightly
different in the `TerminalDispatch` version, since they don't return a
pointless `success` value, and in the case of the getter, the
`TextAttribute` is returned directly instead of by reference.
Ultimately I'd like to move the `AdaptDispatch` code towards that way
of doing things too, but I'd like to deal with that later as part of a
wider refactoring of the `ConGetSet` interface.
* The `SetIndexedForeground256` and `SetIndexedBackground256` calls
required the color indices to be remapped in the `AdaptDispatch`
implementation, because the conhost color table is in a different
order to the XTerm standard. `TerminalDispatch` doesn't have that
problem, so doesn't require the mapping.
* The index color constants used in the 16-color `SetIndexedForeground`
and `SetIndexedBackground` calls are also slightly different for the
same reason.
VALIDATION
I cherry-picked this code on top of the #6506 and #6698 PRs, since
that's only way to really get the different color formats passed-through
to the terminal. I then ran a bunch of manual tests with various color
coverage scripts that I have, and confirmed that all the different color
formats were being rendered as expected.
Closes #6725
2020-07-01 20:13:42 +02:00
|
|
|
void Terminal::SetTextAttributes(const TextAttribute& attrs) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
_buffer->SetCurrentAttributes(attrs);
|
|
|
|
}
|
|
|
|
|
2021-04-16 18:26:28 +02:00
|
|
|
Viewport Terminal::GetBufferSize() noexcept
|
|
|
|
{
|
|
|
|
return _buffer->GetSize();
|
|
|
|
}
|
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::SetCursorPosition(short x, short y) noexcept
|
|
|
|
try
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
const auto viewport = _GetMutableViewport();
|
|
|
|
const auto viewOrigin = viewport.Origin();
|
|
|
|
const short absoluteX = viewOrigin.X + x;
|
|
|
|
const short absoluteY = viewOrigin.Y + y;
|
2019-06-11 22:27:09 +02:00
|
|
|
COORD newPos{ absoluteX, absoluteY };
|
2019-05-03 00:29:04 +02:00
|
|
|
viewport.Clamp(newPos);
|
|
|
|
_buffer->GetCursor().SetPosition(newPos);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
COORD Terminal::GetCursorPosition() noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
const auto absoluteCursorPos = _buffer->GetCursor().GetPosition();
|
|
|
|
const auto viewport = _GetMutableViewport();
|
|
|
|
const auto viewOrigin = viewport.Origin();
|
|
|
|
const short relativeX = absoluteCursorPos.X - viewOrigin.X;
|
|
|
|
const short relativeY = absoluteCursorPos.Y - viewOrigin.Y;
|
|
|
|
COORD newPos{ relativeX, relativeY };
|
|
|
|
|
|
|
|
// TODO assert that the coord is > (0, 0) && <(view.W, view.H)
|
|
|
|
return newPos;
|
|
|
|
}
|
|
|
|
|
2020-07-31 00:24:59 +02:00
|
|
|
bool Terminal::SetCursorColor(const COLORREF color) noexcept
|
|
|
|
try
|
|
|
|
{
|
|
|
|
_buffer->GetCursor().SetColor(color);
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2020-07-31 00:24:59 +02:00
|
|
|
|
Remove unneeded VT-specific control character handling (#4289)
## Summary of the Pull Request
This PR removes all of the VT-specific functionality from the `WriteCharsLegacy` function that dealt with control characters, since those controls are now handled in the state machine when in VT mode. It also removes most of the control character handling from the `Terminal::_WriteBuffer` method for the same reason.
## References
This is a followup to PR #4171
## PR Checklist
* [x] Closes #3971
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Requires documentation to be updated
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: https://github.com/microsoft/terminal/issues/780#issuecomment-570287435
## Detailed Description of the Pull Request / Additional comments
There are four changes to the `WriteCharsLegacy` implementation:
1. The `TAB` character had special case handling in VT mode which is now no longer required. This fixes a bug in the Python REPL editor (when run from a cmd shell in Windows Terminal), which would prevent you tabbing past the end of the line. It also fixes #3971.
2. Following on from point 1, the `WC_NONDESTRUCTIVE_TAB` flag could also now be removed. It only ever applied in VT mode, in which case the `TAB` character isn't handled in `WriteCharsLegacy`, so there isn't a need for a non-destructive version.
3. There used to be special case handling for a `BS` character at the beginning of the line when in VT mode, and that is also no longer required. This fixes an edge-case bug which would prevent a glyph being output for code point 8 when `ENABLE_PROCESSED_OUTPUT` was disabled.
4. There was quite a lot of special case handling for control characters in the "end-of-line wrap" implementation, which is no longer required. This fixes a bug which would prevent "low ASCII" characters from wrapping when output at the end of a line.
Then in the `Terminal::_WriteBuffer` implementation, I've simply removed all control character handling, except for `LF`. The Terminal is always in VT mode, so the control characters are always handled by the state machine. The exception for the `LF` character is simply because it doesn't have a proper implementation yet, so it still passes the character through to `_WriteBuffer`. That will get cleaned up eventually, but I thought that could wait for a later PR.
Finally, with the removal of the VT mode handling in `WriteCharsLegacy`, there was no longer a need for the `SCREEN_INFORMATION::InVTMode` method to be publicly accessible. That has now been made private.
## Validation Steps Performed
I've only tested manually, making sure the conhost and Windows Terminal still basically work, and confirming that the above-mentioned bugs are fixed by these changes.
2020-01-29 20:18:46 +01:00
|
|
|
// Method Description:
|
|
|
|
// - Moves the cursor down one line, and possibly also to the leftmost column.
|
|
|
|
// Arguments:
|
|
|
|
// - withReturn, set to true if a carriage return should be performed as well.
|
|
|
|
// Return value:
|
|
|
|
// - true if succeeded, false otherwise
|
|
|
|
bool Terminal::CursorLineFeed(const bool withReturn) noexcept
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto cursorPos = _buffer->GetCursor().GetPosition();
|
Add support for "reflow"ing the Terminal buffer (#4741)
This PR adds support for "Resize with Reflow" to the Terminal. In
conhost, `ResizeWithReflow` is the function that's responsible for
reflowing wrapped lines of text as the buffer gets resized. Now that
#4415 has merged, we can also implement this in the Terminal. Now, when
the Terminal is resized, it will reflow the lines of it's buffer in the
same way that conhost does. This means, the terminal will no longer chop
off the ends of lines as the buffer is too small to represent them.
As a happy side effect of this PR, it also fixed #3490. This was a bug
that plagued me during the investigation into this functionality. The
original #3490 PR, #4354, tried to fix this bug with some heavy conpty
changes. Turns out, that only made things worse, and far more
complicated. When I really got to thinking about it, I realized "conhost
can handle this right, why can't the Terminal?". Turns out, by adding
resize with reflow, I was also able to fix this at the same time.
Conhost does a little bit of math after reflowing to attempt to keep the
viewport in the same relative place after a reflow. By re-using that
logic in the Terminal, I was able to fix #3490.
I also included that big ole test from #3490, because everyone likes
adding 60 test cases in a PR.
## References
* #4200 - this scenario
* #405/#4415 - conpty emits wrapped lines, which was needed for this PR
* #4403 - delayed EOL wrapping via conpty, which was also needed for
this
* #4354 - we don't speak of this PR anymore
## PR Checklist
* [x] Closes #1465
* [x] Closes #3490
* [x] Closes #4771
* [x] Tests added/passed
## EDIT: Changes to this PR on 5 March 2020
I learned more since my original version of this PR. I wrote that in
January, and despite my notes that say it was totally working, it
_really_ wasn't.
Part of the hard problem, as mentioned in #3490, is that the Terminal
might request a resize to (W, H-1), and while conpty is preparing that
frame, or before the terminal has received that frame, the Terminal
resizes to (W, H-2). Now, there aren't enough lines in the terminal
buffer to catch all the lines that conpty is about to emit. When that
happens, lines get duplicated in the buffer. From a UX perspective, this
certainly looks a lot worse than a couple lost lines. It looks like
utter chaos.
So I've introduced a new mode to conpty to try and counteract this
behavior. This behavior I'm calling "quirky resize". The **TL;DR** of
quirky resize mode is that conpty won't emit the entire buffer on a
resize, and will trust that the terminal is prepared to reflow it's
buffer on it's own.
This will enable the quirky resize behavior for applications that are
prepared for it. The "quirky resize" is "don't `InvalidateAll` when the
terminal resizes". This is added as a quirk as to not regress other
terminal applications that aren't prepared for this behavior
(gnome-terminal, conhost in particular). For those kinds of terminals,
when the buffer is resized, it's just going to lose lines. That's what
currently happens for them.
When the quirk is enabled, conpty won't repaint the entire buffer. This
gets around the "duplicated lines" issue that requesting multiple
resizes in a row can cause. However, for these terminals that are
unprepared, the conpty cursor might end up in the wrong position after a
quirky resize.
The case in point is maximizing the terminal. For maximizing
(height->50) from a buffer that's 30 lines tall, with the cursor on
y=30, this is what happens:
* With the quirk disabled, conpty reprints the entire buffer. This is
60 lines that get printed. This ends up blowing away about 20 lines
of scrollback history, as the terminal app would have tried to keep
the text pinned to the bottom of the window. The term. app moved the
viewport up 20 lines, and then the 50 lines of conpty output (30
lines of text, and 20 blank lines at the bottom) overwrote the lines
from the scrollback. This is bad, but not immediately obvious, and
is **what currently happens**.
* With the quirk enabled, conpty doesn't emit any lines, but the
actual content of the window is still only in the top 30 lines.
However, the terminal app has still moved 20 lines down from the
scrollback back into the viewport. So the terminal's cursor is at
y=50 now, but conpty's is at 30. This means that the terminal and
conpty are out of sync, and there's not a good way of re-syncing
these. It's very possible (trivial in `powershell`) that the new
output will jump up to y=30 override the existing output in the
terminal buffer.
The Windows Terminal is already prepared for this quirky behavior, so it
doesn't keep the output at the bottom of the window. It shifts it's
viewport down to match what conpty things the buffer looks like.
What happens when we have passthrough mode and WT is like "I would like
quirky resize"? I guess things will just work fine, cause there won't be
a buffer behind the passthrough app that the terminal cares about. Sure,
in the passthrough case the Terminal could _not_ quirky resize, but the
quirky resize won't be wrong.
2020-03-13 01:43:37 +01:00
|
|
|
|
|
|
|
// since we explicitly just moved down a row, clear the wrap status on the
|
|
|
|
// row we just came from
|
2021-01-20 22:16:56 +01:00
|
|
|
_buffer->GetRowByOffset(cursorPos.Y).SetWrapForced(false);
|
Add support for "reflow"ing the Terminal buffer (#4741)
This PR adds support for "Resize with Reflow" to the Terminal. In
conhost, `ResizeWithReflow` is the function that's responsible for
reflowing wrapped lines of text as the buffer gets resized. Now that
#4415 has merged, we can also implement this in the Terminal. Now, when
the Terminal is resized, it will reflow the lines of it's buffer in the
same way that conhost does. This means, the terminal will no longer chop
off the ends of lines as the buffer is too small to represent them.
As a happy side effect of this PR, it also fixed #3490. This was a bug
that plagued me during the investigation into this functionality. The
original #3490 PR, #4354, tried to fix this bug with some heavy conpty
changes. Turns out, that only made things worse, and far more
complicated. When I really got to thinking about it, I realized "conhost
can handle this right, why can't the Terminal?". Turns out, by adding
resize with reflow, I was also able to fix this at the same time.
Conhost does a little bit of math after reflowing to attempt to keep the
viewport in the same relative place after a reflow. By re-using that
logic in the Terminal, I was able to fix #3490.
I also included that big ole test from #3490, because everyone likes
adding 60 test cases in a PR.
## References
* #4200 - this scenario
* #405/#4415 - conpty emits wrapped lines, which was needed for this PR
* #4403 - delayed EOL wrapping via conpty, which was also needed for
this
* #4354 - we don't speak of this PR anymore
## PR Checklist
* [x] Closes #1465
* [x] Closes #3490
* [x] Closes #4771
* [x] Tests added/passed
## EDIT: Changes to this PR on 5 March 2020
I learned more since my original version of this PR. I wrote that in
January, and despite my notes that say it was totally working, it
_really_ wasn't.
Part of the hard problem, as mentioned in #3490, is that the Terminal
might request a resize to (W, H-1), and while conpty is preparing that
frame, or before the terminal has received that frame, the Terminal
resizes to (W, H-2). Now, there aren't enough lines in the terminal
buffer to catch all the lines that conpty is about to emit. When that
happens, lines get duplicated in the buffer. From a UX perspective, this
certainly looks a lot worse than a couple lost lines. It looks like
utter chaos.
So I've introduced a new mode to conpty to try and counteract this
behavior. This behavior I'm calling "quirky resize". The **TL;DR** of
quirky resize mode is that conpty won't emit the entire buffer on a
resize, and will trust that the terminal is prepared to reflow it's
buffer on it's own.
This will enable the quirky resize behavior for applications that are
prepared for it. The "quirky resize" is "don't `InvalidateAll` when the
terminal resizes". This is added as a quirk as to not regress other
terminal applications that aren't prepared for this behavior
(gnome-terminal, conhost in particular). For those kinds of terminals,
when the buffer is resized, it's just going to lose lines. That's what
currently happens for them.
When the quirk is enabled, conpty won't repaint the entire buffer. This
gets around the "duplicated lines" issue that requesting multiple
resizes in a row can cause. However, for these terminals that are
unprepared, the conpty cursor might end up in the wrong position after a
quirky resize.
The case in point is maximizing the terminal. For maximizing
(height->50) from a buffer that's 30 lines tall, with the cursor on
y=30, this is what happens:
* With the quirk disabled, conpty reprints the entire buffer. This is
60 lines that get printed. This ends up blowing away about 20 lines
of scrollback history, as the terminal app would have tried to keep
the text pinned to the bottom of the window. The term. app moved the
viewport up 20 lines, and then the 50 lines of conpty output (30
lines of text, and 20 blank lines at the bottom) overwrote the lines
from the scrollback. This is bad, but not immediately obvious, and
is **what currently happens**.
* With the quirk enabled, conpty doesn't emit any lines, but the
actual content of the window is still only in the top 30 lines.
However, the terminal app has still moved 20 lines down from the
scrollback back into the viewport. So the terminal's cursor is at
y=50 now, but conpty's is at 30. This means that the terminal and
conpty are out of sync, and there's not a good way of re-syncing
these. It's very possible (trivial in `powershell`) that the new
output will jump up to y=30 override the existing output in the
terminal buffer.
The Windows Terminal is already prepared for this quirky behavior, so it
doesn't keep the output at the bottom of the window. It shifts it's
viewport down to match what conpty things the buffer looks like.
What happens when we have passthrough mode and WT is like "I would like
quirky resize"? I guess things will just work fine, cause there won't be
a buffer behind the passthrough app that the terminal cares about. Sure,
in the passthrough case the Terminal could _not_ quirky resize, but the
quirky resize won't be wrong.
2020-03-13 01:43:37 +01:00
|
|
|
|
Remove unneeded VT-specific control character handling (#4289)
## Summary of the Pull Request
This PR removes all of the VT-specific functionality from the `WriteCharsLegacy` function that dealt with control characters, since those controls are now handled in the state machine when in VT mode. It also removes most of the control character handling from the `Terminal::_WriteBuffer` method for the same reason.
## References
This is a followup to PR #4171
## PR Checklist
* [x] Closes #3971
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Requires documentation to be updated
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: https://github.com/microsoft/terminal/issues/780#issuecomment-570287435
## Detailed Description of the Pull Request / Additional comments
There are four changes to the `WriteCharsLegacy` implementation:
1. The `TAB` character had special case handling in VT mode which is now no longer required. This fixes a bug in the Python REPL editor (when run from a cmd shell in Windows Terminal), which would prevent you tabbing past the end of the line. It also fixes #3971.
2. Following on from point 1, the `WC_NONDESTRUCTIVE_TAB` flag could also now be removed. It only ever applied in VT mode, in which case the `TAB` character isn't handled in `WriteCharsLegacy`, so there isn't a need for a non-destructive version.
3. There used to be special case handling for a `BS` character at the beginning of the line when in VT mode, and that is also no longer required. This fixes an edge-case bug which would prevent a glyph being output for code point 8 when `ENABLE_PROCESSED_OUTPUT` was disabled.
4. There was quite a lot of special case handling for control characters in the "end-of-line wrap" implementation, which is no longer required. This fixes a bug which would prevent "low ASCII" characters from wrapping when output at the end of a line.
Then in the `Terminal::_WriteBuffer` implementation, I've simply removed all control character handling, except for `LF`. The Terminal is always in VT mode, so the control characters are always handled by the state machine. The exception for the `LF` character is simply because it doesn't have a proper implementation yet, so it still passes the character through to `_WriteBuffer`. That will get cleaned up eventually, but I thought that could wait for a later PR.
Finally, with the removal of the VT mode handling in `WriteCharsLegacy`, there was no longer a need for the `SCREEN_INFORMATION::InVTMode` method to be publicly accessible. That has now been made private.
## Validation Steps Performed
I've only tested manually, making sure the conhost and Windows Terminal still basically work, and confirming that the above-mentioned bugs are fixed by these changes.
2020-01-29 20:18:46 +01:00
|
|
|
cursorPos.Y++;
|
|
|
|
if (withReturn)
|
|
|
|
{
|
|
|
|
cursorPos.X = 0;
|
|
|
|
}
|
|
|
|
_AdjustCursorPosition(cursorPos);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
Remove unneeded VT-specific control character handling (#4289)
## Summary of the Pull Request
This PR removes all of the VT-specific functionality from the `WriteCharsLegacy` function that dealt with control characters, since those controls are now handled in the state machine when in VT mode. It also removes most of the control character handling from the `Terminal::_WriteBuffer` method for the same reason.
## References
This is a followup to PR #4171
## PR Checklist
* [x] Closes #3971
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Requires documentation to be updated
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: https://github.com/microsoft/terminal/issues/780#issuecomment-570287435
## Detailed Description of the Pull Request / Additional comments
There are four changes to the `WriteCharsLegacy` implementation:
1. The `TAB` character had special case handling in VT mode which is now no longer required. This fixes a bug in the Python REPL editor (when run from a cmd shell in Windows Terminal), which would prevent you tabbing past the end of the line. It also fixes #3971.
2. Following on from point 1, the `WC_NONDESTRUCTIVE_TAB` flag could also now be removed. It only ever applied in VT mode, in which case the `TAB` character isn't handled in `WriteCharsLegacy`, so there isn't a need for a non-destructive version.
3. There used to be special case handling for a `BS` character at the beginning of the line when in VT mode, and that is also no longer required. This fixes an edge-case bug which would prevent a glyph being output for code point 8 when `ENABLE_PROCESSED_OUTPUT` was disabled.
4. There was quite a lot of special case handling for control characters in the "end-of-line wrap" implementation, which is no longer required. This fixes a bug which would prevent "low ASCII" characters from wrapping when output at the end of a line.
Then in the `Terminal::_WriteBuffer` implementation, I've simply removed all control character handling, except for `LF`. The Terminal is always in VT mode, so the control characters are always handled by the state machine. The exception for the `LF` character is simply because it doesn't have a proper implementation yet, so it still passes the character through to `_WriteBuffer`. That will get cleaned up eventually, but I thought that could wait for a later PR.
Finally, with the removal of the VT mode handling in `WriteCharsLegacy`, there was no longer a need for the `SCREEN_INFORMATION::InVTMode` method to be publicly accessible. That has now been made private.
## Validation Steps Performed
I've only tested manually, making sure the conhost and Windows Terminal still basically work, and confirming that the above-mentioned bugs are fixed by these changes.
2020-01-29 20:18:46 +01:00
|
|
|
|
2019-07-31 01:28:28 +02:00
|
|
|
// Method Description:
|
2019-12-19 23:12:53 +01:00
|
|
|
// - deletes count characters starting from the cursor's current position
|
2019-07-31 01:28:28 +02:00
|
|
|
// - it moves over the remaining text to 'replace' the deleted text
|
|
|
|
// - for example, if the buffer looks like this ('|' is the cursor): [abc|def]
|
|
|
|
// - calling DeleteCharacter(1) will change it to: [abc|ef],
|
|
|
|
// - i.e. the 'd' gets deleted and the 'ef' gets shifted over 1 space and **retain their previous text attributes**
|
|
|
|
// Arguments:
|
2019-12-19 23:12:53 +01:00
|
|
|
// - count, the number of characters to delete
|
2019-07-31 01:28:28 +02:00
|
|
|
// Return value:
|
|
|
|
// - true if succeeded, false otherwise
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::DeleteCharacter(const size_t count) noexcept
|
|
|
|
try
|
2019-07-31 01:28:28 +02:00
|
|
|
{
|
|
|
|
SHORT dist;
|
2019-12-19 23:12:53 +01:00
|
|
|
if (!SUCCEEDED(SizeTToShort(count, &dist)))
|
2019-07-31 01:28:28 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
|
|
|
const auto copyToPos = cursorPos;
|
|
|
|
const COORD copyFromPos{ cursorPos.X + dist, cursorPos.Y };
|
2020-01-03 19:44:27 +01:00
|
|
|
const auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X;
|
2019-07-31 01:28:28 +02:00
|
|
|
SHORT width;
|
|
|
|
if (!SUCCEEDED(UIntToShort(sourceWidth, &width)))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a rectangle of the source
|
|
|
|
auto source = Viewport::FromDimensions(copyFromPos, width, 1);
|
|
|
|
|
|
|
|
// Get a rectangle of the target
|
|
|
|
const auto target = Viewport::FromDimensions(copyToPos, source.Dimensions());
|
|
|
|
const auto walkDirection = Viewport::DetermineWalkDirection(source, target);
|
|
|
|
|
|
|
|
auto sourcePos = source.GetWalkOrigin(walkDirection);
|
|
|
|
auto targetPos = target.GetWalkOrigin(walkDirection);
|
|
|
|
|
|
|
|
// Iterate over the source cell data and copy it over to the target
|
|
|
|
do
|
|
|
|
{
|
|
|
|
const auto data = OutputCell(*(_buffer->GetCellDataAt(sourcePos)));
|
|
|
|
_buffer->Write(OutputCellIterator({ &data, 1 }), targetPos);
|
|
|
|
} while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-07-31 01:28:28 +02:00
|
|
|
|
|
|
|
// Method Description:
|
2019-12-19 23:12:53 +01:00
|
|
|
// - Inserts count spaces starting from the cursor's current position, moving over the existing text
|
2019-07-31 01:28:28 +02:00
|
|
|
// - for example, if the buffer looks like this ('|' is the cursor): [abc|def]
|
|
|
|
// - calling InsertCharacter(1) will change it to: [abc| def],
|
|
|
|
// - i.e. the 'def' gets shifted over 1 space and **retain their previous text attributes**
|
|
|
|
// Arguments:
|
2019-12-19 23:12:53 +01:00
|
|
|
// - count, the number of spaces to insert
|
2019-07-31 01:28:28 +02:00
|
|
|
// Return value:
|
|
|
|
// - true if succeeded, false otherwise
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::InsertCharacter(const size_t count) noexcept
|
|
|
|
try
|
2019-07-31 01:28:28 +02:00
|
|
|
{
|
|
|
|
// NOTE: the code below is _extremely_ similar to DeleteCharacter
|
|
|
|
// We will want to use this same logic and implement a helper function instead
|
|
|
|
// that does the 'move a region from here to there' operation
|
|
|
|
// TODO: Github issue #2163
|
|
|
|
SHORT dist;
|
2019-12-19 23:12:53 +01:00
|
|
|
if (!SUCCEEDED(SizeTToShort(count, &dist)))
|
2019-07-31 01:28:28 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
|
|
|
const auto copyFromPos = cursorPos;
|
|
|
|
const COORD copyToPos{ cursorPos.X + dist, cursorPos.Y };
|
2020-01-03 19:44:27 +01:00
|
|
|
const auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X;
|
2019-07-31 01:28:28 +02:00
|
|
|
SHORT width;
|
|
|
|
if (!SUCCEEDED(UIntToShort(sourceWidth, &width)))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a rectangle of the source
|
|
|
|
auto source = Viewport::FromDimensions(copyFromPos, width, 1);
|
|
|
|
const auto sourceOrigin = source.Origin();
|
|
|
|
|
|
|
|
// Get a rectangle of the target
|
|
|
|
const auto target = Viewport::FromDimensions(copyToPos, source.Dimensions());
|
|
|
|
const auto walkDirection = Viewport::DetermineWalkDirection(source, target);
|
|
|
|
|
|
|
|
auto sourcePos = source.GetWalkOrigin(walkDirection);
|
|
|
|
auto targetPos = target.GetWalkOrigin(walkDirection);
|
|
|
|
|
|
|
|
// Iterate over the source cell data and copy it over to the target
|
|
|
|
do
|
|
|
|
{
|
|
|
|
const auto data = OutputCell(*(_buffer->GetCellDataAt(sourcePos)));
|
|
|
|
_buffer->Write(OutputCellIterator({ &data, 1 }), targetPos);
|
|
|
|
} while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection));
|
2020-01-03 19:44:27 +01:00
|
|
|
const auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), dist);
|
2019-07-31 01:28:28 +02:00
|
|
|
_buffer->Write(eraseIter, cursorPos);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-07-31 01:28:28 +02:00
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::EraseCharacters(const size_t numChars) noexcept
|
|
|
|
try
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
const auto absoluteCursorPos = _buffer->GetCursor().GetPosition();
|
|
|
|
const auto viewport = _GetMutableViewport();
|
|
|
|
const short distanceToRight = viewport.RightExclusive() - absoluteCursorPos.X;
|
|
|
|
const short fillLimit = std::min(static_cast<short>(numChars), distanceToRight);
|
2020-01-03 19:44:27 +01:00
|
|
|
const auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), fillLimit);
|
2019-05-03 00:29:04 +02:00
|
|
|
_buffer->Write(eraseIter, absoluteCursorPos);
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-05-03 00:29:04 +02:00
|
|
|
|
2019-07-31 01:28:28 +02:00
|
|
|
// Method description:
|
|
|
|
// - erases a line of text, either from
|
|
|
|
// 1. beginning to the cursor's position
|
|
|
|
// 2. cursor's position to end
|
|
|
|
// 3. beginning to end
|
|
|
|
// - depending on the erase type
|
|
|
|
// Arguments:
|
|
|
|
// - the erase type
|
|
|
|
// Return value:
|
|
|
|
// - true if succeeded, false otherwise
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept
|
|
|
|
try
|
2019-07-31 01:28:28 +02:00
|
|
|
{
|
|
|
|
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
|
|
|
const auto viewport = _GetMutableViewport();
|
|
|
|
COORD startPos = { 0 };
|
|
|
|
startPos.Y = cursorPos.Y;
|
|
|
|
// nlength determines the number of spaces we need to write
|
|
|
|
DWORD nlength = 0;
|
|
|
|
|
|
|
|
// Determine startPos.X and nlength by the eraseType
|
|
|
|
switch (eraseType)
|
|
|
|
{
|
|
|
|
case DispatchTypes::EraseType::FromBeginning:
|
|
|
|
nlength = cursorPos.X - viewport.Left() + 1;
|
|
|
|
break;
|
|
|
|
case DispatchTypes::EraseType::ToEnd:
|
|
|
|
startPos.X = cursorPos.X;
|
2019-09-24 22:05:39 +02:00
|
|
|
nlength = viewport.RightExclusive() - startPos.X;
|
2019-07-31 01:28:28 +02:00
|
|
|
break;
|
|
|
|
case DispatchTypes::EraseType::All:
|
|
|
|
startPos.X = viewport.Left();
|
2019-09-24 22:05:39 +02:00
|
|
|
nlength = viewport.RightExclusive() - startPos.X;
|
2019-07-31 01:28:28 +02:00
|
|
|
break;
|
Refactor VT parameter handling (#7799)
This PR introduces a pair of classes for managing VT parameters that
automatically handle range checking and default fallback values, so the
individual operations don't have to do that validation themselves. In
addition to simplifying the code, this fixes a few cases where we were
mishandling missing or extraneous parameters, and adds support for
parameter sequences on commands that couldn't previously handle them.
This PR also sets a limit on the number of parameters allowed, to help
thwart DoS memory consumption attacks.
## References
* The new parameter class also introduces the concept of an
omitted/default parameter which is not necessarily zero, which is a
prerequisite for addressing issue #4417.
## Detailed Description of the Pull Request / Additional comments
There are two new classes provide by this PR: a `VTParameter` class,
similar in function to a `std::optional<size_t>`, which holds an
individual parameter (which may be an omitted/default value); and a
`VTParameters` class, similar in function to `gsl:span<VTParameter>`,
which holds a sequence of those parameters.
Where `VTParameter` differs from `std::optional` is with the inclusion
of two cast operators. There is a `size_t` cast that interprets omitted
and zero values as 1 (the expected behaviour for most numeric
parameters). And there is a generic cast, for use with the enum
parameter types, which interprets omitted values as 0 (the expected
behaviour for most selective parameters).
The advantage of `VTParameters` class is that it has an `at` method that
can never fail - out of range values simply return the a default
`VTParameter` instance (this is standard behaviour in VT terminals). It
also has a `size` method that will always return a minimum count of 1,
since an empty parameter list is typically the equivalent of a single
"default" parameter, so this guarantees you'll get at least one value
when iterating over the list with `size()`.
For cases where we just need to call the same dispatch method for every
parameter, there is a helper `for_each` method, which repeatedly calls a
given predicate function with each value in the sequence. It also
collates the returned success values to determine the overall result of
the sequence. As with the `size` method, this will always make at least
one call, so it correctly handles empty sequences.
With those two classes in place, we could get rid of all the parameter
validation and default handling code in the `OutputStateMachineEngine`.
We now just use the `VTParameters::at` method to grab a parameter and
typically pass it straight to the appropriate dispatch method, letting
the cast operators automatically handle the assignment of default
values. Occasionally we might need a `value_or` call to specify a
non-standard default value, but those cases are fairly rare.
In some case the `OutputStateMachineEngine` was also checking whether
parameters values were in range, but for the most part this shouldn't
have been necessary, since that is something the dispatch classes would
already have been doing themselves (in the few cases that they weren't,
I've now updated them to do so).
I've also updated the `InputStateMachineEngine` in a similar way to the
`OutputStateMachineEngine`, getting rid of a few of the parameter
extraction methods, and simplifying other parts of the implementation.
It's not as clean a replacement as the output engine, but there are
still benefits in using the new classes.
## Validation Steps Performed
For the most part I haven't had to alter existing tests other than
accounting for changes to the API. There were a couple of tests I needed
to drop because they were checking for failure cases which shouldn't
have been failing (unexpected parameters should never be an error), or
testing output engine validation that is no longer handled at that
level.
I've added a few new tests to cover operations that take sequences of
selective parameters (`ED`, `EL`, `TBC`, `SM`, and `RM`). And I've
extended the cursor movement tests to make sure those operations can
handle extraneous parameters that weren't expected. I've also added a
test to verify that the state machine will correctly ignore parameters
beyond the maximum 32 parameter count limit.
I've also manual confirmed that the various test cases given in issues
#2101 are now working as expected.
Closes #2101
2020-10-15 18:12:52 +02:00
|
|
|
default:
|
2019-07-31 01:28:28 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
const auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), nlength);
|
2019-10-18 02:13:53 +02:00
|
|
|
|
|
|
|
// Explicitly turn off end-of-line wrap-flag-setting when erasing cells.
|
|
|
|
_buffer->Write(eraseIter, startPos, false);
|
2019-07-31 01:28:28 +02:00
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-07-31 01:28:28 +02:00
|
|
|
|
|
|
|
// Method description:
|
|
|
|
// - erases text in the buffer in two ways depending on erase type
|
|
|
|
// 1. 'erases' all text visible to the user (i.e. the text in the viewport)
|
|
|
|
// 2. erases all the text in the scrollback
|
|
|
|
// Arguments:
|
|
|
|
// - the erase type
|
|
|
|
// Return Value:
|
|
|
|
// - true if succeeded, false otherwise
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::EraseInDisplay(const DispatchTypes::EraseType eraseType) noexcept
|
|
|
|
try
|
2019-07-31 01:28:28 +02:00
|
|
|
{
|
|
|
|
// Store the relative cursor position so we can restore it later after we move the viewport
|
|
|
|
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
2020-01-03 19:44:27 +01:00
|
|
|
#pragma warning(suppress : 26496) // This is written by ConvertToOrigin, cpp core checks is wrong saying it should be const.
|
2019-07-31 01:28:28 +02:00
|
|
|
auto relativeCursor = cursorPos;
|
|
|
|
_mutableViewport.ConvertToOrigin(&relativeCursor);
|
|
|
|
|
|
|
|
// Initialize the new location of the viewport
|
|
|
|
// the top and bottom parameters are determined by the eraseType
|
|
|
|
SMALL_RECT newWin;
|
|
|
|
newWin.Left = _mutableViewport.Left();
|
|
|
|
newWin.Right = _mutableViewport.RightExclusive();
|
|
|
|
|
|
|
|
if (eraseType == DispatchTypes::EraseType::All)
|
|
|
|
{
|
|
|
|
// In this case, we simply move the viewport down, effectively pushing whatever text was on the screen into the scrollback
|
|
|
|
// and thus 'erasing' the text visible to the user
|
|
|
|
const auto coordLastChar = _buffer->GetLastNonSpaceCharacter(_mutableViewport);
|
|
|
|
if (coordLastChar.X == 0 && coordLastChar.Y == 0)
|
|
|
|
{
|
|
|
|
// Nothing to clear, just return
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
short sNewTop = coordLastChar.Y + 1;
|
|
|
|
|
|
|
|
// Increment the circular buffer only if the new location of the viewport would be 'below' the buffer
|
|
|
|
const short delta = (sNewTop + _mutableViewport.Height()) - (_buffer->GetSize().Height());
|
|
|
|
for (auto i = 0; i < delta; i++)
|
|
|
|
{
|
|
|
|
_buffer->IncrementCircularBuffer();
|
|
|
|
sNewTop--;
|
|
|
|
}
|
|
|
|
|
|
|
|
newWin.Top = sNewTop;
|
|
|
|
newWin.Bottom = sNewTop + _mutableViewport.Height();
|
|
|
|
}
|
|
|
|
else if (eraseType == DispatchTypes::EraseType::Scrollback)
|
|
|
|
{
|
|
|
|
// We only want to erase the scrollback, and leave everything else on the screen as it is
|
|
|
|
// so we grab the text in the viewport and rotate it up to the top of the buffer
|
|
|
|
COORD scrollFromPos{ 0, 0 };
|
|
|
|
_mutableViewport.ConvertFromOrigin(&scrollFromPos);
|
|
|
|
_buffer->ScrollRows(scrollFromPos.Y, _mutableViewport.Height(), -scrollFromPos.Y);
|
|
|
|
|
|
|
|
// Since we only did a rotation, the text that was in the scrollback is now _below_ where we are going to move the viewport
|
|
|
|
// and we have to make sure we erase that text
|
2020-01-03 19:44:27 +01:00
|
|
|
const auto eraseStart = _mutableViewport.Height();
|
|
|
|
const auto eraseEnd = _buffer->GetLastNonSpaceCharacter(_mutableViewport).Y;
|
2019-07-31 01:28:28 +02:00
|
|
|
for (SHORT i = eraseStart; i <= eraseEnd; i++)
|
|
|
|
{
|
2019-08-01 22:19:22 +02:00
|
|
|
_buffer->GetRowByOffset(i).Reset(_buffer->GetCurrentAttributes());
|
2019-07-31 01:28:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the scroll offset now because there's nothing for the user to 'scroll' to
|
|
|
|
_scrollOffset = 0;
|
|
|
|
|
|
|
|
newWin.Top = 0;
|
|
|
|
newWin.Bottom = _mutableViewport.Height();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
ci: run spell check in CI, fix remaining issues (#4799)
This commit introduces a github action to check our spelling and fixes
the following misspelled words so that we come up green.
It also renames TfEditSes to TfEditSession, because Ses is not a word.
currently, excerpt, fallthrough, identified, occurred, propagate,
provided, rendered, resetting, separate, succeeded, successfully,
terminal, transferred, adheres, breaks, combining, preceded,
architecture, populated, previous, setter, visible, window, within,
appxmanifest, hyphen, control, offset, powerpoint, suppress, parsing,
prioritized, aforementioned, check in, build, filling, indices, layout,
mapping, trying, scroll, terabyte, vetoes, viewport, whose
2020-03-25 19:02:53 +01:00
|
|
|
// Move the viewport, adjust the scroll bar if needed, and restore the old cursor position
|
2019-07-31 01:28:28 +02:00
|
|
|
_mutableViewport = Viewport::FromExclusive(newWin);
|
|
|
|
Terminal::_NotifyScrollEvent();
|
|
|
|
SetCursorPosition(relativeCursor.X, relativeCursor.Y);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2020-10-01 03:00:06 +02:00
|
|
|
|
|
|
|
bool Terminal::WarningBell() noexcept
|
|
|
|
try
|
|
|
|
{
|
|
|
|
_pfnWarningBell();
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-07-31 01:28:28 +02:00
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::SetWindowTitle(std::wstring_view title) noexcept
|
|
|
|
try
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
2020-06-09 23:47:13 +02:00
|
|
|
if (!_suppressApplicationTitle)
|
|
|
|
{
|
|
|
|
_title.emplace(title);
|
|
|
|
_pfnTitleChanged(_title.value());
|
|
|
|
}
|
2019-05-03 00:29:04 +02:00
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Updates the value in the colortable at index tableIndex to the new color
|
2019-12-19 23:12:53 +01:00
|
|
|
// color. color is a COLORREF, format 0x00BBGGRR.
|
2019-05-03 00:29:04 +02:00
|
|
|
// Arguments:
|
|
|
|
// - tableIndex: the index of the color table to update.
|
2019-12-19 23:12:53 +01:00
|
|
|
// - color: the new COLORREF to use as that color table value.
|
2019-05-03 00:29:04 +02:00
|
|
|
// Return Value:
|
|
|
|
// - true iff we successfully updated the color table entry.
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept
|
|
|
|
try
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
2020-01-03 19:44:27 +01:00
|
|
|
_colorTable.at(tableIndex) = color;
|
2019-12-14 01:41:04 +01:00
|
|
|
|
2020-01-03 19:44:27 +01:00
|
|
|
// Repaint everything - the colors might have changed
|
|
|
|
_buffer->GetRenderTarget().TriggerRedrawAll();
|
|
|
|
return true;
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-05-23 19:44:27 +02:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Sets the cursor style to the given style.
|
|
|
|
// Arguments:
|
|
|
|
// - cursorStyle: the style to be set for the cursor
|
|
|
|
// Return Value:
|
|
|
|
// - true iff we successfully set the cursor style
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) noexcept
|
2019-05-23 19:44:27 +02:00
|
|
|
{
|
2020-01-03 19:44:27 +01:00
|
|
|
CursorType finalCursorType = CursorType::Legacy;
|
|
|
|
bool shouldBlink = false;
|
2019-05-23 19:44:27 +02:00
|
|
|
|
|
|
|
switch (cursorStyle)
|
|
|
|
{
|
2020-09-04 22:36:09 +02:00
|
|
|
case DispatchTypes::CursorStyle::UserDefault:
|
|
|
|
finalCursorType = _defaultCursorShape;
|
|
|
|
shouldBlink = true;
|
|
|
|
break;
|
2019-05-23 19:44:27 +02:00
|
|
|
case DispatchTypes::CursorStyle::BlinkingBlock:
|
|
|
|
finalCursorType = CursorType::FullBox;
|
2020-01-03 19:44:27 +01:00
|
|
|
shouldBlink = true;
|
2019-05-23 19:44:27 +02:00
|
|
|
break;
|
|
|
|
case DispatchTypes::CursorStyle::SteadyBlock:
|
|
|
|
finalCursorType = CursorType::FullBox;
|
2020-01-03 19:44:27 +01:00
|
|
|
shouldBlink = false;
|
2019-05-23 19:44:27 +02:00
|
|
|
break;
|
|
|
|
case DispatchTypes::CursorStyle::BlinkingUnderline:
|
|
|
|
finalCursorType = CursorType::Underscore;
|
2020-01-03 19:44:27 +01:00
|
|
|
shouldBlink = true;
|
2019-05-23 19:44:27 +02:00
|
|
|
break;
|
|
|
|
case DispatchTypes::CursorStyle::SteadyUnderline:
|
|
|
|
finalCursorType = CursorType::Underscore;
|
2020-01-03 19:44:27 +01:00
|
|
|
shouldBlink = false;
|
2019-05-23 19:44:27 +02:00
|
|
|
break;
|
|
|
|
case DispatchTypes::CursorStyle::BlinkingBar:
|
|
|
|
finalCursorType = CursorType::VerticalBar;
|
2020-01-03 19:44:27 +01:00
|
|
|
shouldBlink = true;
|
2019-05-23 19:44:27 +02:00
|
|
|
break;
|
|
|
|
case DispatchTypes::CursorStyle::SteadyBar:
|
|
|
|
finalCursorType = CursorType::VerticalBar;
|
2020-01-03 19:44:27 +01:00
|
|
|
shouldBlink = false;
|
2019-05-23 19:44:27 +02:00
|
|
|
break;
|
2020-09-04 22:36:09 +02:00
|
|
|
|
2019-05-23 19:44:27 +02:00
|
|
|
default:
|
2020-09-04 22:36:09 +02:00
|
|
|
// Invalid argument should be ignored.
|
|
|
|
return true;
|
2019-05-23 19:44:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
_buffer->GetCursor().SetType(finalCursorType);
|
2020-01-03 19:44:27 +01:00
|
|
|
_buffer->GetCursor().SetBlinkingAllowed(shouldBlink);
|
2019-05-23 19:44:27 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2019-05-24 18:53:00 +02:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Updates the default foreground color from a COLORREF, format 0x00BBGGRR.
|
|
|
|
// Arguments:
|
2019-12-19 23:12:53 +01:00
|
|
|
// - color: the new COLORREF to use as the default foreground color
|
2019-05-24 18:53:00 +02:00
|
|
|
// Return Value:
|
|
|
|
// - true
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::SetDefaultForeground(const COLORREF color) noexcept
|
|
|
|
try
|
2019-05-24 18:53:00 +02:00
|
|
|
{
|
2019-12-19 23:12:53 +01:00
|
|
|
_defaultFg = color;
|
2019-05-24 18:53:00 +02:00
|
|
|
|
|
|
|
// Repaint everything - the colors might have changed
|
|
|
|
_buffer->GetRenderTarget().TriggerRedrawAll();
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2019-05-24 18:53:00 +02:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Updates the default background color from a COLORREF, format 0x00BBGGRR.
|
|
|
|
// Arguments:
|
2019-12-19 23:12:53 +01:00
|
|
|
// - color: the new COLORREF to use as the default background color
|
2019-05-24 18:53:00 +02:00
|
|
|
// Return Value:
|
|
|
|
// - true
|
2020-01-03 19:44:27 +01:00
|
|
|
bool Terminal::SetDefaultBackground(const COLORREF color) noexcept
|
|
|
|
try
|
2019-05-24 18:53:00 +02:00
|
|
|
{
|
2019-12-19 23:12:53 +01:00
|
|
|
_defaultBg = color;
|
|
|
|
_pfnBackgroundColorChanged(color);
|
2019-05-24 18:53:00 +02:00
|
|
|
|
|
|
|
// Repaint everything - the colors might have changed
|
|
|
|
_buffer->GetRenderTarget().TriggerRedrawAll();
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2020-03-13 01:44:28 +01:00
|
|
|
|
Split `TermControl` into a Core, Interactivity, and Control layer (#9820)
## Summary of the Pull Request
Brace yourselves, it's finally here. This PR does the dirty work of splitting the monolithic `TermControl` into three components. These components are:
* `ControlCore`: This encapsulates the `Terminal` instance, the `DxEngine` and `Renderer`, and the `Connection`. This is intended to everything that someone might need to stand up a terminal instance in a control, but without any regard for how the UX works.
* `ControlInteractivity`: This is a wrapper for the `ControlCore`, which holds the logic for things like double-click, right click copy/paste, selection, etc. This is intended to be a UI framework-independent abstraction. The methods this layer exposes can be called the same from both the WinUI TermControl and the WPF control.
* `TermControl`: This is the UWP control. It's got a Core and Interactivity inside it, which it uses for the actual logic of the terminal itself. TermControl's main responsibility is now
By splitting into smaller pieces, it will enable us to
* write unit tests for the `Core` and `Interactivity` bits, which we desparately need
* Combine `ControlCore` and `ControlInteractivity` in an out-of-proc core process in the future, to enable tab tearout.
However, we're not doing that work quite yet. There's still lots of work to be done to enable that, thought this is likely the biggest portion.
Ideally, this would just be methods moved wholesale from one file to another. Unfortunately, there are a bunch of cases where that didn't work as well as expected. Especially when trying to better enforce the boundary between the classes.
We've got a couple tests here that I've added. These are partially examples, and partially things I ran into while implementing this. A bunch of things from #7001 can go in now that we have this.
This PR is gonna be a huge pain to review - 38 files with 3,730 additions and 1,661 deletions is nothing to scoff at. It will also conflict 100% with anything that's targeting `TermControl`. I'm hoping we can review this over the course of the next week and just be done with it, and leave plenty of runway for 1.9 bugs in post.
## References
* In pursuit of #1256
* Proc Model: #5000
* https://github.com/microsoft/terminal/projects/5
## PR Checklist
* [x] Closes #6842
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50760249
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50760258
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
## Detailed Description of the Pull Request / Additional comments
* I don't love the names `ControlCore` and `ControlInteractivity`. Open to other names.
* I added a `ICoreState` interface for "properties that come from the `ControlCore`, but consumers of the `TermControl` need to know". In the future, these will all need to be handled specially, because they might involve an RPC call to retrieve the info from the core (or cache it) in the window process.
* I've added more `EventArgs` to make more events proper `TypedEvent`s.
* I've changed how the TerminalApp layer requests updated TaskbarProgress state. It doesn't need to pump TermControl to raise a new event anymore.
* ~~Something that snuck into this branch in the very long history is the switch to `DCompositionCreateSurfaceHandle` for the `DxEngine`. @miniksa wrote this originally in 30b8335, I'm just finally committing it here. We'll need that in the future for the out-of-proc stuff.~~
* I reverted this in c113b65d9. We can revert _that_ commit when we want to come back to it.
* I've changed the acrylic handler a decent amount. But added tests!
* All the `ThrottledFunc` things are left in `TermControl`. Some might be able to move down into core/interactivity, but once we figure out how to use a different kind of Dispatcher (because a UI thread won't necessarily exist for those components).
* I've undoubtably messed up the merging of the locking around the appearance config stuff recently
## Validation Steps Performed
I've got a rolling list in https://github.com/microsoft/terminal/issues/6842#issuecomment-810990460 that I'm updating as I go.
2021-04-27 17:50:45 +02:00
|
|
|
til::color Terminal::GetDefaultBackground() const noexcept
|
|
|
|
{
|
|
|
|
return _defaultBg;
|
|
|
|
}
|
|
|
|
|
2021-10-26 23:12:22 +02:00
|
|
|
bool Terminal::SetInputMode(const TerminalInput::Mode mode, const bool enabled) noexcept
|
|
|
|
try
|
2020-03-13 01:44:28 +01:00
|
|
|
{
|
2021-10-26 23:12:22 +02:00
|
|
|
_terminalInput->SetInputMode(mode, enabled);
|
2020-03-13 01:44:28 +01:00
|
|
|
return true;
|
|
|
|
}
|
2021-10-26 23:12:22 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2020-03-13 01:44:28 +01:00
|
|
|
|
2020-07-09 13:25:30 +02:00
|
|
|
bool Terminal::SetScreenMode(const bool reverseMode) noexcept
|
|
|
|
try
|
|
|
|
{
|
|
|
|
_screenReversed = reverseMode;
|
|
|
|
|
|
|
|
// Repaint everything - the colors will have changed
|
|
|
|
_buffer->GetRenderTarget().TriggerRedrawAll();
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2020-07-09 13:25:30 +02:00
|
|
|
|
2021-01-22 06:11:11 +01:00
|
|
|
bool Terminal::EnableXtermBracketedPasteMode(const bool enabled) noexcept
|
|
|
|
{
|
|
|
|
_bracketedPasteMode = enabled;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept
|
|
|
|
{
|
|
|
|
return _bracketedPasteMode;
|
|
|
|
}
|
|
|
|
|
2020-03-13 01:44:28 +01:00
|
|
|
bool Terminal::IsVtInputEnabled() const noexcept
|
|
|
|
{
|
|
|
|
// We should never be getting this call in Terminal.
|
|
|
|
FAIL_FAST();
|
|
|
|
}
|
2020-03-13 18:39:42 +01:00
|
|
|
|
|
|
|
bool Terminal::SetCursorVisibility(const bool visible) noexcept
|
|
|
|
{
|
|
|
|
_buffer->GetCursor().SetIsVisible(visible);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Terminal::EnableCursorBlinking(const bool enable) noexcept
|
|
|
|
{
|
|
|
|
_buffer->GetCursor().SetBlinkingAllowed(enable);
|
|
|
|
|
|
|
|
// GH#2642 - From what we've gathered from other terminals, when blinking is
|
|
|
|
// disabled, the cursor should remain On always, and have the visibility
|
|
|
|
// controlled by the IsVisible property. So when you do a printf "\e[?12l"
|
|
|
|
// to disable blinking, the cursor stays stuck On. At this point, only the
|
|
|
|
// cursor visibility property controls whether the user can see it or not.
|
|
|
|
// (Yes, the cursor can be On and NOT Visible)
|
|
|
|
_buffer->GetCursor().SetIsOn(true);
|
|
|
|
return true;
|
|
|
|
}
|
2020-06-30 03:55:40 +02:00
|
|
|
|
|
|
|
bool Terminal::CopyToClipboard(std::wstring_view content) noexcept
|
|
|
|
try
|
|
|
|
{
|
|
|
|
_pfnCopyToClipboard(content);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-09 20:28:06 +02:00
|
|
|
CATCH_RETURN_FALSE()
|
2020-09-03 19:52:39 +02:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Updates the buffer's current text attributes to start a hyperlink
|
|
|
|
// Arguments:
|
|
|
|
// - The hyperlink URI
|
|
|
|
// - The customID provided (if there was one)
|
|
|
|
// Return Value:
|
|
|
|
// - true
|
|
|
|
bool Terminal::AddHyperlink(std::wstring_view uri, std::wstring_view params) noexcept
|
|
|
|
{
|
|
|
|
auto attr = _buffer->GetCurrentAttributes();
|
2020-10-17 00:08:59 +02:00
|
|
|
const auto id = _buffer->GetHyperlinkId(uri, params);
|
2020-09-03 19:52:39 +02:00
|
|
|
attr.SetHyperlinkId(id);
|
|
|
|
_buffer->SetCurrentAttributes(attr);
|
|
|
|
_buffer->AddHyperlinkToMap(uri, id);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Updates the buffer's current text attributes to end a hyperlink
|
|
|
|
// Return Value:
|
|
|
|
// - true
|
|
|
|
bool Terminal::EndHyperlink() noexcept
|
|
|
|
{
|
|
|
|
auto attr = _buffer->GetCurrentAttributes();
|
|
|
|
attr.SetHyperlinkId(0);
|
|
|
|
_buffer->SetCurrentAttributes(attr);
|
|
|
|
return true;
|
|
|
|
}
|
2020-11-18 23:24:11 +01:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Updates the taskbar progress indicator
|
|
|
|
// Arguments:
|
|
|
|
// - state: indicates the progress state
|
|
|
|
// - progress: indicates the progress value
|
|
|
|
// Return Value:
|
|
|
|
// - true
|
2021-05-05 20:12:55 +02:00
|
|
|
bool Terminal::SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) noexcept
|
2020-11-18 23:24:11 +01:00
|
|
|
{
|
2021-05-05 20:12:55 +02:00
|
|
|
_taskbarState = static_cast<size_t>(state);
|
|
|
|
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case DispatchTypes::TaskbarState::Clear:
|
|
|
|
// Always set progress to 0 in this case
|
|
|
|
_taskbarProgress = 0;
|
|
|
|
break;
|
|
|
|
case DispatchTypes::TaskbarState::Set:
|
|
|
|
// Always set progress to the value given in this case
|
|
|
|
_taskbarProgress = progress;
|
|
|
|
break;
|
|
|
|
case DispatchTypes::TaskbarState::Indeterminate:
|
|
|
|
// Leave the progress value unchanged in this case
|
|
|
|
break;
|
|
|
|
case DispatchTypes::TaskbarState::Error:
|
|
|
|
case DispatchTypes::TaskbarState::Paused:
|
|
|
|
// In these 2 cases, if the given progress value is 0, then
|
|
|
|
// leave the progress value unchanged, unless the current progress
|
|
|
|
// value is 0, in which case set it to a 'minimum' value (10 in our case);
|
|
|
|
// if the given progress value is greater than 0, then set the progress value
|
|
|
|
if (progress == 0)
|
|
|
|
{
|
|
|
|
if (_taskbarProgress == 0)
|
|
|
|
{
|
|
|
|
_taskbarProgress = TaskbarMinProgress;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_taskbarProgress = progress;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-11-18 23:24:11 +01:00
|
|
|
if (_pfnTaskbarProgressChanged)
|
|
|
|
{
|
|
|
|
_pfnTaskbarProgressChanged();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2021-01-11 19:01:38 +01:00
|
|
|
|
|
|
|
bool Terminal::SetWorkingDirectory(std::wstring_view uri) noexcept
|
|
|
|
{
|
|
|
|
_workingDirectory = uri;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring_view Terminal::GetWorkingDirectory() noexcept
|
|
|
|
{
|
|
|
|
return _workingDirectory;
|
|
|
|
}
|
2021-02-18 03:31:52 +01:00
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Saves the current text attributes to an internal stack.
|
|
|
|
// Arguments:
|
|
|
|
// - options, cOptions: if present, specify which portions of the current text attributes
|
|
|
|
// should be saved. Only a small subset of GraphicsOptions are actually supported;
|
|
|
|
// others are ignored. If no options are specified, all attributes are stored.
|
|
|
|
// Return Value:
|
|
|
|
// - true
|
|
|
|
bool Terminal::PushGraphicsRendition(const VTParameters options) noexcept
|
|
|
|
{
|
|
|
|
_sgrStack.Push(_buffer->GetCurrentAttributes(), options);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Restores text attributes from the internal stack. If only portions of text attributes
|
|
|
|
// were saved, combines those with the current attributes.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - true
|
|
|
|
bool Terminal::PopGraphicsRendition() noexcept
|
|
|
|
{
|
|
|
|
const TextAttribute current = _buffer->GetCurrentAttributes();
|
|
|
|
_buffer->SetCurrentAttributes(_sgrStack.Pop(current));
|
|
|
|
return true;
|
|
|
|
}
|