Compare commits

..

140 Commits

Author SHA1 Message Date
Dustin Howett
21f91b1d7a Move WPFTerminalControl and TestNetCore to NET 8 2024-10-15 15:44:52 -05:00
Dustin L. Howett
990ed187d6 ci: fix the code formatting job (#18059)
We started requiring PowerShell 7+ in #18021

We did not update the code formatting task.
2024-10-15 11:58:51 -05:00
Michael Xu
494bc5bd3f Ensure OpenConsole.psm1 requires PowerShell 7 (#18021)
Closes #17505
2024-10-10 21:27:34 -05:00
Carlos Zamora
36f064cfc8 Fix hiding the icon when it's set to "none" (#18030)
The settings UI and settings model allow you to set the icon to "none"
to hide the icon (you can actually see this effect in the settings UI
when changing the value of the profile icon). However, during settings
validation, "none" is considered a file path, which is then failed to be
parsed, resulting in the icon being marked as invalid and immediately
clearing the value.

This PR fixes this issue by considering "none" to be an accepted value
during validation.

Related to #15843
Closes #17943

## Validation Steps Performed
When an icon is set to "none", ...
 no more warning
 the icon is hidden
2024-10-10 19:11:51 -05:00
Michael Xu
d0e94365d0 Focus tabs to the right-not left-when the active tab is closed (#18022)
Closes #17244
2024-10-11 00:08:03 +00:00
Carlos Zamora
18d86bca09 Add a Compatibility and Terminal page to the Settings UI (#17895)
## Summary of the Pull Request
Adds a global Compatibility page to the settings UI. This page exposes
several existing settings and introduces a few new settings:
- compatibility.allowHeadless
- compatibility.isolatedMode
- compatibility.textMeasurement
- debugFeatures

This also adds a Terminal subpage for profiles in the settings UI. This
page includes:
- suppressApplicationTitle
- compatibility.input.forceVT
- compatibility.allowDECRQCRA
- answerbackMessage

Several smaller changes were accomplished as a part of this PR:
- `experimental.input.forceVT` was renamed to
`compatibility.input.forceVT`
- introduced the `compatibility.allowDECRQCRA` setting
- updated the schema for these new settings and
`compatibility.allowHeadless` (which was missing)
- add `Feature_DebugModeUI` feature flag to control if debug features
should be shown in the SUI

Verified accessible via Accessibility Insights

A part of #10000
Closes #16672
2024-10-10 23:54:31 +00:00
Carlos Zamora
0b4d3d5f89 Add miscellaneous simple settings to the settings UI (#17923)
## Summary of the Pull Request
Adds the following settings to the settings UI:
- $profile.RainbowSuggestions
- $profile.CellWidth
- $global.SearchWebDefaultQueryUrl
- $global.EnableColorSelection
- $global.ShowAdminShield
- $global.EnableUnfocusedAcrylic

Additionally, the following settings have graduated from experimental 🎓:
- $profile.rightClickContextMenu

Part of #10000
2024-10-10 18:14:55 -05:00
Leonard Hecker
4386bf07fd Avoid focus loops in ConPTY (#17829)
This fixes a lot of subtle issues:
* Avoid emitting another de-/iconify VT sequence when
  we encounter a (de)iconify VT sequence during parsing.
* Avoid emitting a de-/iconify VT sequence when
  a focus event is received on the signal pipe.
* Avoid emitting such sequences on startup.
* Avoid emitting multiple such sequences
  when rapidly un-/focusing the window.

It's also a minor cleanup, because the `GA_ROOTOWNER` is not security
relevant. It was added because there was concern that someone can just
spawn a ConPTY session, tell it that it's focused, and spawn a child
which is now focused. But why would someone do that, when the console
IOCTLs to do so are not just publicly available but also documented?

I also disabled the IME window.

## Validation Steps Performed
* First:
  ```cpp
  int main() {
      for (bool show = false;; show = !show) {
          printf(show ? "Show in 3s...\n" : "Hide in 3s...\n");
          Sleep(3000);
          ShowWindow(GetConsoleWindow(), show ? SW_SHOW : SW_HIDE);
      }
  }
  ```
* PowerShell 5's `Get-Credential` gains focus 
* `sleep 5; Get-Credential` and focus another app. WT should start
  blinking in the taskbar. Restore it. The popup has focus 
* Run `:hardcopy` in vim: Window is shown centered at (0,0) ✖️
  But that's okay because it does that already anyway 
* `Connect-AzAccount` doesn't crash PowerShell 
2024-10-08 11:37:33 -05:00
James Holderness
aa256ad5c9 Add support for the S8C1T/S7C1T escape sequences (#17945)
This PR adds support for the `S8C1T` and `S7C1T` commands, which enable
an application to choose whether the terminal should use C1 controls
when sending key sequences and query responses.

This also updates the `DOCS` command to set both the input and output
code pages. So when switched to ISO2022 mode, the C1 controls will be
transmitted as 8-bit, which is what legacy systems would be expecting.

## Detailed Description of the Pull Request / Additional comments

While adding the input code page support, I also reworked the way we
handle the code page reset in `RIS`. In the original implementation we
saved the active code page when the `DOCS` sequence was first used, and
that would become the default value for a reset.

With this PR I'm now saving the code pages whenever `SetConsoleCP` or
`SetConsoleOutputCP` is called, so those APIs now control what the
default values will be. This feels more consistent than the previous
approach. And this is how WSL sets its initial code page to UTF-8.

## Validation Steps Performed

I've added a couple of unit tests that check one of each applicable C1
control in the key sequences and query reports.

I also built myself a code page aware telnet client so I could log into
WSL in 8-bit mode, and confirmed that the C1 transmissions are working
as expected in vttest.

Closes #17931
Tests added/passed
2024-10-07 13:11:38 +00:00
Dustin L. Howett
b715008de3 Revert "Stop updating AutoSuggestBox on selection" (#17989)
Reverts microsoft/terminal#17961
Closes #17987 
Reopens #17916
2024-10-03 17:24:38 +02:00
Windows Console Service Bot
9278873d76 Localization Updates - main - 09/27/2024 03:04:44 (#17966) 2024-10-01 16:48:54 -05:00
Windows Console Service Bot
59dc5eff42 Localization Updates - main - 09/26/2024 19:14:21 (#17958)
Closes #17752
Closes #17764
Closes #17830
2024-09-26 14:22:05 -05:00
Leonard Hecker
bcac9993cb Stop updating AutoSuggestBox on selection (#17961)
`AutoSuggestBox` has a `SuggestionChosen` event and any reasonable
person would assume that this means one of the items was chosen.
But with WinUI it's raised whenever a suggestion is merely highlighted.
`QuerySubmitted` is the right event instead. Clearly that naming is
a lot better than `SuggestionChosen`, since the property to get the
chosen item is called `ChosenSuggestion`.
WinUI, like the unrelenting wilderness of a world indifferent to human
suffering, stands as a testament to the futility of human aspiration.

Closes #17916

## Validation Steps Performed
* Type "Casc"
* Move up/down with the arrow keys
* Neither the filtered list nor the text updates 
* Press Enter on an item
* Text updates 
2024-09-26 10:06:01 -05:00
Leonard Hecker
a8e0b9ccf6 Fix an exception on startup (#17960)
It bothered me. :)

## Validation Steps Performed
* Launch packaged WT. `IsPackaged() == true` 
* Launch unpackaged WT. `IsPackaged() == false` 
2024-09-25 10:49:40 -07:00
Carlos Zamora
37aba3157c Add Warnings to Settings UI (#17933)
Adds the following settings to the Interaction page under a Warnings subsection:
- ConfirmCloseAllTabs
- InputServiceWarning
- WarnAboutLargePaste
- WarnAboutMultiLinePaste

This also changes the JSON keys of those settings to be in the `warning` namespace as a QOL change for JSON users. We still handle the legacy keys, don't worry 😉.

#10000
2024-09-25 10:10:24 -07:00
Carlos Zamora
0bd19e9cfc Improve color contrast of reset button in SUI (#17912)
Adds a theme resource for the color of the reset button in the settings UI.

Closes #17902
2024-09-25 10:07:25 -07:00
Leonard Hecker
fc606d2bae Add input scope startup setting (#17953)
This adds a "defaultInputScope" setting, hooks it up to our TSF,
and exposes it as a setting in the UI under the startup page.
In order to stay close with the other language setting, I moved that
one from the appearance to the startup page as well.
20 out of the 26 files in this PR are boilerplate unfortunately.

Closes #17816

## Validation Steps Performed
* Install and use the Chinese IME
* Launch WT
* Chinese input 
* Change setting to `alphanumericHalfWidth`
* Restart WT
* English input 
2024-09-24 16:14:31 -05:00
Leonard Hecker
4259ce535f Fix clear buffer command (#17884)
Without a VT "renderer" there's no implicit output anymore when
calling `ClearPseudoConsole`. The fix is trivial, but it works
slightly different from before: Previously, we would preserve
the line the cursor is on, while this PR doesn't do that.
I felt like there's not much merit in preserving the line,
because it may be a multi-line prompt which won't work with that.

Closes #17867

## Validation Steps Performed
Bind 3 different actions to the 3 variants of "Clear buffer"
and test them. They work. 
2024-09-24 14:11:27 -05:00
Leonard Hecker
d9131c6889 Stop scrolling on output when search is open (#17885)
* Don't reset the position entirely when changing the needle
* Don't change the scroll position when output arrives
* Don't interfere with the search when output arrives constantly

Closes #17301

## Validation Steps Performed
* In pwsh, run `10000..20000 | % { sleep 0.25; $_ }`
  * You can search for e.g. `1004` and it'll find 10 results. 
  * You can scroll up and down past it and it won't snap back
    when new output arrives. 
* `while ($true) { Write-Host -NoNewline "`e[Ha"; sleep 0.0001; }`
  * You can cycle between the hits effortlessly.  (This tests that
    the constantly reset `OutputIdle` event won't interfere.)
* On input change, the focused result is near the previous one. 
2024-09-24 14:06:36 -05:00
Leonard Hecker
0ce654eaf6 Fix a cooked read deadlock (#17905)
Because `_layoutLine` would never return `column == columnLimit` for
control character visualizers, we'd get a deadlock in `_redisplay`,
as it tries to fill the line until it's full, but never achieve it.

Closes #17893

## Validation Steps Performed
* Press Ctrl-A to insert "^A"
* Press Home to get to the start of the prompt
* Press and hold "A" until the line wraps
* The line wraps and there's no deadlock 
2024-09-24 14:06:01 -05:00
James Holderness
fc586e2662 Fix a sixel crash when the buffer is reflowed (#17951)
## Summary of the Pull Request

The sixel parser has an internal buffer that holds the indexed-color
representation of the image, prior to it being translated to RGB. This
buffer only retains the section of the image that is within the visible
viewport, so we're continually erasing segments from the top of it when
the image is large enough to trigger a scroll.

But there is a problem that arises if the window or font is resized so
that the buffer needs to reflow, because that can result in the image
being pushed entirely offscreen. At that point the segment we're trying
to erase is actually larger than the buffer itself, which can end up
causing the terminal to crash

To fix this, we just need to check for an oversized erase attempt and
simply clear the buffer instead.

## Validation Steps Performed

I could easily reproduce this crash in Windows Terminal by resizing the
font while viewing an animated gif with img2sixel. With this PR applied
the crash no longer occurs.

## PR Checklist
- [x] Closes #17947
2024-09-24 14:04:28 -05:00
Leonard Hecker
b520da26d4 Check EnableHexNumpad before enabling it (#17954)
This just adds a quick registry check for `EnableHexNumpad`.

Depends on #17774
Closes #17762 (again)

## Validation Steps Performed
* Alt + NumpadAdd + 221E doesn't do anything 
* Set the `EnableHexNumpad` registry key
* Restart
* Alt + NumpadAdd + 221E inserts ∞ 
2024-09-24 13:56:30 -05:00
Carlos Zamora
a7e47b711a Fix text scaling issues in settings UI (#17910)
## Summary of the Pull Request
Fixes some issues with truncated text in the settings UI when 200% text
scaling is applied.

For #17897, a minimum height was applied instead of a plain "height".
This ensures that the desired height is applied in general, but under
200% text scaling, we are allowed to grow past that, thus preventing the
truncation of the text.

For #17898, flyouts have a scroll viewer inside them by default. We
actually don't want the scroll viewer because that means the text will
appear "truncated" when in reality, the user is expected to notice the
small scrollbar and scroll horizontally (why that's the default, I will
never know). This PR introduces a new style that can be applied to these
flyouts to cause text wrapping instead of horizontal scrolling. Looked
through the app for any instances where this happens.

For #12006, simply changing the column width from a static value to
"auto" fixes the issue. Frankly, we care more about the text appearing
as a whole (and as whole words). The name of the actions wrap properly
anyways.

Closes #17897
Closes #17898
Closes #12006
2024-09-17 19:45:59 +02:00
Dustin L. Howett
2c97c0555d build: fix the TSA configuration (#17929)
We are, quite literally, shipping the org chart.
2024-09-17 10:30:59 -05:00
James Holderness
5e8e10fdc0 Add support for resetting the color scheme with RIS (#17879)
## Summary of the Pull Request

This improves our `RIS` (hard reset) implementation, so it now also
resets any changes that are made to the color table and color aliases,
which is one of the things it's supposed to be doing.

## References and Relevant Issues

This is also a small step towards implementing the `OSC` sequences that
reset individual color table entries (issue #3719).

## Detailed Description of the Pull Request / Additional comments

The way this works is by having a second copy of the color table and
alias indices to hold the default values in the `RenderSettings` class.
This default set is initially populated at startup with the user's
chosen color scheme, but can also potentially be updated if the user
changes their settings while a session is already in progress.

When we receive an `RIS` request, we just copy the default values back
over the active settings, and refresh the renderer.

## Validation Steps Performed

I've manually tested both OpenConsole and Windows Terminal by changing
my color scheme programmatically, and then confirming that the original
colors are restored when an `RIS` sequence is received.

I've also added some basic unit tests that check both the color aliases
and color table are restored by `RIS`.

## PR Checklist
- [x] Tests added/passed
2024-09-16 13:59:12 -05:00
Leonard Hecker
bc6f3e2275 Fix a crash on pane close (#17886)
The underlying issue is that the "Pane" is used both as a model and as
a UI element and so a pane loses its content as soon as it is closed,
but the tree only gets reordered after the animation has finished.
This PR is truly just a hotfix, because it doesn't solve this issue,
it only adds checks to the function that crashes.

Closes #17869
Closes #17871

## Validation Steps Performed
* `Split pane` a few times
* Run the "Close all other panes" action
* Doesn't crash 
2024-09-12 10:03:39 -05:00
Carlos Zamora
6196a3d0f7 Add Feature_QuickFix to preview builds (#17888) 2024-09-11 08:34:11 -07:00
Dustin L. Howett
4aa1624cd2 Remove PackageES in favor of our own versioning package (#17872)
PackageES is deprecated by known scourge-on-earth OneBranch, and is now
the cause of some non-compliance.

I got permission from them to open-source it, so that's coming next.

For now, we can just depend on a package based on our code based on
theirs.

Tested and working for C++ (DLL, EXE), C#, NuGet and MSIX.
2024-09-10 01:37:25 +02:00
Mike Griese
c699a468c9 Add Feature_SaveSnippet to preview builds (#17881)
whoops. This should have been in preview after I sorted out #17366


----

I did this one on GH so let's hope CI works
2024-09-10 01:37:12 +02:00
Kacper Michajłow
0576e5bc1e Allow closing tabs with middle mouse button when close button is hidden (#15924)
## Summary of the Pull Request
This commit fixes the middle mouse button handler. The `PointerReleased` callback is registered, but it is not operational because, on the Release event, the mouse button is no longer pressed. We need to track its state and act accordingly.

Issue was introduced by commit 05e7ea1423, which changed the event handler from `PointerPressed` to `PointerReleased`, rendering it inoperative. Instead, the default handler is used. The main issue is that when the close button is hidden with the `showCloseButton` option, the default handler no longer closes the tab on middle mouse clicks.

Also made it consistent with the Settings tab, which was never converted to `PointerReleased` and is still handled with a custom handler.

## References and Relevant Issues
Related commit 05e7ea1423

## Validation Steps Performed
I've been using this commit locally for quite some time, figured out I might as well share it.
2024-09-09 14:08:25 -07:00
Nihat Uygar Köseer
544452dad4 Add tab color indicator for tab switch menu (CTRL+Tab) (#17820)
Added tab color indicator for the tab switch menu. Tab color indicators
have the same color as the background color of the tabs. If a tab has
the default background color, the indicator is not shown in the tab
switch menu.

Closes #17465
2024-09-06 10:21:40 -05:00
Leonard Hecker
00f46e400a Fix crash in AppHost::_QuitRequested (#17848) 2024-09-06 10:19:03 -05:00
Leonard Hecker
4eb06fee07 Restore contents when a screen info is closed (#17853) 2024-09-06 10:18:51 -05:00
Leonard Hecker
d2c3cfd164 Fix ScrollRect to DECCRA translation (#17849)
By translating the clip rectangle into a source-relative coordinate
space we can calculate the intersection that must be copied
much much more easily. I should've done that from the start.

Closes #17801

## Validation Steps Performed
* Test code provided in #17801
2024-09-04 16:06:36 -07:00
Leonard Hecker
5fdfd51209 Dedup command history by default (#17852)
Under ConPTY we don't load any user settings. `SetUpConsole` notes:
> If we are [ConPTY], we don't want to load any user settings,
> because that could result in some strange rendering results [...]

This enables deduplication by default, which I figured wouldn't cause
any regressions since it's a user-controllable setting anyway, while
it's clearly something the average user wants enabled, for the same
reason that PSReadLine has HistoryNoDuplicates enabled by default.

Closes #17797

## Validation Steps Performed
* Launch conhost, enter 2 commands, press F7, select the older one,
  press Enter, press F7. 2 entries 
* Launch WT, enter 2 commands, press F7, select the older one,
  press Enter, press F7. 2 entries 
2024-09-04 12:57:23 -07:00
Leonard Hecker
7b50f12a78 Avoid dropping Esc characters from VT responses (#17833)
`GetChar` checks if the vkey is VK_ESCAPE. `CharToKeyEvents` however
tries really hard to figure out the vkeys of all characters.
To avoid these issues all we need to do is to simply use the existing
`WriteString` function we already use for all other VT responses.
If it's good for conhost responses, it's good for ConPTY responses.

Additionally, this removes another `IsVtInputEnabled` which was
redundant with `WriteString` which worked exactly the same internally.

Closes #17813
Closes #17851
Probably also related to #17823

## Validation Steps Performed
* Wrote a small app to send and receive a DA1 request. It works 
* WSL already worked to begin with (and still works now) 
* No double-encoding of mouse input events 
2024-09-04 15:47:01 +02:00
James Holderness
6e5827add5 Pass through DCS responses when VT input mode is disabled (#17845)
## Summary of the Pull Request

When an app makes a VT request that returns a `DCS` response, and it
hasn't also enabled VT input mode, the new passthrough implementation
loses that response. All the app receives is an `Alt`+`\` key press
triggered by the `ST` terminator. This PR fixes that issue.

## References and Relevant Issues

This is one of the unresolved issues tracked in #17643.

## Detailed Description of the Pull Request / Additional comments

The way `DCS` sequences are handled in the input state machine engine is
by returning a nullptr from `ActionDcsDispatch`, which tells the state
machine to ignore that content. But the sequence is still buffered, and
when the `ST` terminator is eventually received, that buffer is flushed,
which passes the whole thing through to the app.

Originally this only worked when VT input mode was enabled, otherwise
the `ST` sequence is converted into a key press, and the buffered `DCS`
content is lost. The way it works now is we set a flag when the `DCS`
introducer is received, and if that flag is set when the `ST` arrives,
we know to trigger a flush rather a key press.

## Validation Steps Performed

I've tested a `DA3` request from the cmd shell (i.e. `echo ^[[=c`), and
confirmed that now works as expected. I've also hacked Windows Terminal
to disable win32-input mode, so I could check how it works with conpty
clients generating standard VT input, and confirmed that an `Alt`+`\`
keypress is still translated correctly.
2024-09-04 13:36:32 +00:00
Mike Griese
17a55da0f9 Fix two ConPTY HWND focus issues (#17828)
Worked with @ekoschik on this one. 

## Bug the first: the MSAL window `ixptools` spawns

> The auth prompt in pwsh.exe is disabling the terminal window while its
opened and re-enabling it when the window closes. BUT it is enabling
Terminal after dismissing itself, instead of before, which means
terminal is disabled when activated.
> 
> Terminal wants focus on the ISLAND window (a grandchild; island is
parented to bridge, which is parented to terminal’s TLW). When it is
activated, it gets a `WM_SETFOCUS` (in response to DefWindowProc
`WM_ACTIVATE`). From `WM_SETFOCUS` it calls `SetFocus` on the bridge
window, and similarly the bridge calls `SetFocus` on the island.
> 
> If the TLW is disabled, these `SetFocus` calls fail (see [this
check](#internal-link-redacted) in `SetFocus`). In the case above, this
leaves Terminal’s TLW as focus, and it doesn’t handle keyboard input.
Note that the window IS foreground/active, but because focus is not on
the island it doesn’t see the keyboard input. Another thing to note is
that clicking on the space to the right of the tabs does NOT revive
keyboard input, but clicking on the tabs or main area does.

> **I recommend having the TLW handle WM_ENABLE and call SetFocus on the
island window.**

And guess what, that works!

## Bug the second: When sublime text is the git `EDITOR`, it doesn't
toss focus back to the Terminal


> In this case, Sublime is calling SFW on the pseudo console window. I
don’t have its code, but it is presumably doing something like
SetForegroundWindow(GetConsoleWindow()). This queues an event to the
pseudo window, and when that event is processed the pseudo window
becomes the active and focus window on the queue (which is shared with
Terminal).
> 
> The sublime window dismisses itself and does the above SFW call.
Dismissing immediately activates the Terminal TLW, which does the
triple-focus dance (TLW sets focus on itself, then bridge, then island).
This completes but is overwritten immediately when the pseudo window
activates itself. Note that the pseudo window is active at this point
(not the terminal window).

> **I recommend having the Pseudo console window handle WM_ACTIVATE by
calling SetFocus on the island window (and not passing the message to
DefWindowProc).**

And guess what, that works!


----

Closes #15956 (I did test this)
This might be related to #13388, we'll have folks try canary and check
2024-08-29 21:19:15 +00:00
Leonard Hecker
0cb3426281 Give spacing marks space (#17826)
Spacing marks are called so, because they have a positive advance
width, unlike their non-spacing neighbors (as the name indicates).
After this we stop assigning such gc=Mc codepoints a zero width.

Closes #17810
2024-08-29 15:27:24 -05:00
PankajBhojwani
1482fd4ecd Add action IDs to the color selection commands (#17821)
## Summary of the Pull Request
Add action IDs to the default commands for color selection

## Validation Steps Performed
Color selection commands now show up in the command palette

## PR Checklist
- [x] Closes #17819
- [ ] Tests added/passed
- [ ] Documentation updated
- If checked, please file a pull request on [our docs
repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
- [ ] Schema updated (if necessary)
2024-08-29 15:08:28 -05:00
Nihat Uygar Köseer
837215b206 Handle window resize event (CSI t, resize) (#17721)
`ResizeWindow` event in `TerminalApi` is handled and bubbled to
`TerminalApi->ControlCore->TermControl->TerminalPage->AppHost`. Resizing
is accepted only if the window is not in fullscreen or quake mode, and
has 1 tab and pane.

Relevant issues: #5094
2024-08-29 13:43:50 -05:00
Carlos Zamora
93d592bb41 Remove unnecessary FMT (#17795)
There's an unnecessary `fmt::format` here caught in the code review: https://github.com/microsoft/terminal/pull/17678#discussion_r1729426705

This simply removes it.
2024-08-26 14:38:11 -07:00
Leonard Hecker
760daa642e Modernize VtPipeTerm (#17647)
This shortens VtPipeTerm quite a bit, which used to have various debug
flags and modes. I kept the `--out` flag to redirect the output to a
file, but I removed the `--debug` (pipe the output through WSL and
show escape sequences visually) and `--headless` (hide conpty) flags.

I did this, because VtPipeTerm always used the system ConPTY API
but I needed it to use my local OpenConsole. I also wanted it to
use overlapped IO for testing but found that it was too difficult
to refactor make that work.

I also noticed that the project was the only holdout for
`conpty.h` which had to be kept in sync with `winconpty.h`.
2024-08-26 21:06:43 +02:00
James Holderness
1f71568c2a Make sure a blank title string resets the title (#17802)
## Summary of the Pull Request

When a VT title sequence sets the title to a blank string, that is meant
to trigger a reset to the default starting value. This used to work in
the past because the blank value was dealt with by conhost, so Windows
Terminal never received a blank title, but that's no longer the case
with the new VT passthrough. This PR fixes the issue by getting Windows
Terminal to handle the blank title strings itself. 

## References and Relevant Issues

VT passthrough was introduced in PR #17510.

## Validation Steps Performed

I've manually verified that the `OSC 0`, `OSC 2`, and `DECSWT` sequences
now correctly reset the title when passed a blank title string. 

## PR Checklist
- [x] Closes #17800
2024-08-26 14:02:39 +02:00
Dustin L. Howett
f93347ed4b version: bump to 1.23 on main 2024-08-23 15:10:12 -05:00
Leonard Hecker
040f26175f Use DECCRA/DECFRA for ScrollConsoleScreenBuffer (#17747)
This adds logic to get the DA1 report from the hosting terminal on
startup. We then use the information to figure out if it supports
rectangular area operations. If so, we can use DECCRA/DECFRA to
implement ScrollConsoleScreenBuffer.

This additionally changes `ScrollConsoleScreenBuffer` to always
forbid control characters as the fill character, even in conhost
(via `VtIo::SanitizeUCS2`). My hope is that this makes the API
more consistent and robust as it avoids another source for
invisible control characters in the text buffer.

Part of #17643

## Validation Steps Performed
* New tests 
2024-08-23 22:02:01 +02:00
Windows Console Service Bot
0a91023df8 Localization Updates - main - 08/22/2024 03:06:16 (#17767) 2024-08-23 12:30:13 -07:00
Carlos Zamora
0a9cbd09d8 Track and log changes to settings (#17678)
Adds functionality throughout the settings model to keep track of which
settings have been set.

There are two entry points:
- AppLogic.cpp: this is where we perform a settings reload by loading
the JSON
- MainPage.cpp: this is where the Save button is clicked in the settings
UI

Both of these entry points call into
`CascadiaSettings::LogSettingChanges()` where we aggregate the list of
changes (specifically, _which_ settings changed, not _what_ their value
is).

Just about all of the settings model objects now have a
`LogSettingChanges(std::set& changes, std::string_view context)` on
them.
- `changes` is where we aggregate all of the changes to. In it being a
set, we don't need to worry about duplicates and can do things like
iterate across all of the profiles.
- `context` prepends a string to the setting. This'll allow us to better
identify where a setting was changes (i.e. "global.X" are global
settings). We also use this to distinguish between settings set in the
~base layer~ profile defaults vs individual profiles.

The change log in each object is modified via two ways:
- `LayerJson()` changes: this is useful for detecting JSON changes! All
we're doing is checking if the setting has a value (due to inheritance,
just about everything is an optional here!). If the value is set, we add
the json key to the change log
- `INHERITABLE_SETTING_WITH_LOGGING` in IInheritable.h: we already use
this macro to define getters and setters. This new macro updates the
setter to check if the value was set to something different. If so, log
it!

 Other notes:
- We're not distinguishing between `defaultAppearance` and
`unfocusedAppearance`
- We are distinguishing between `profileDefaults` and `profile` (any
other profile)
- New Tab Menu Customization:
- we really just care about the entry types. Handled in
`GlobalAppSettings`
- Font:
- We still have support for legacy values here. We still want to track
them, but just use the modern keys.
- `Theme`:
- We don't do inheritance here, so we have to approach it differently.
During the JSON load, we log each setting. However, we don't have
`LayerJson`! So instead, do the work in `CascadiaSettings` and store the
changes there. Note that we don't track any changes made via setters.
This is fine for now since themes aren't even in the settings UI, so we
wouldn't get much use out of it anyways.
- Actions:
- Actions are weird because we can have nested and iterable actions too,
but `ActionsAndArgs` as a whole add a ton of functionality. I handled it
over in `Command::LogSettingChanges` and we generally just serialize it
to JSON to get the keys. It's a lot easier than dealing with the object
model.

Epic: #10000
Auto-Save (ish): #12424
2024-08-23 12:28:19 -07:00
Mike Griese
cd8c12586b Clip the "current commandline" at the cursor position (#17781)
This is particularly relevant to pwsh with the "ghost text" enabled. In
that scenario, pwsh writes out the predicted command to the right of the
cursor. With `showSuggestions(useCommandline=true)`, we'd auto-include
that text in the filter, and that was effectively useless.

This instead defaults us to not use anything to the right of the cursor
(inclusive) for what we consider "the current commandline"

closes #17772
2024-08-23 12:24:34 -07:00
Mike Griese
ef960558b3 A trio of snippets pane fixes (#17794)
1. Don't crash on a cmdpal "duplicate pane" of a snippets pane
   * Found while trying to solve bug the third. 
* "Duplicate pane" with a snippets pane would crash. This was due to us
attempting to `PreviewText` when there was no buffer yet.
(`_activeBuffer()` strikes again)
2. dismiss the preview from cmdpal correctly too
   * Again while looking for part the third, I hit this
* I have a `sendInput(input: "a")` command. This is the first command in
the palette. And opening a new pane would... preview that command in the
new pane? weird. Moving the line in `CommandPalette::_close` fixes this
3. Don't crash when we're restoring a snippets pane and there's a bunch
of windows
   * This was the real bug I was trying to fix
* Looks like if you have enough panes & windows, there's enough of a
delay between ctoring a snippets pane and actually calling
`_UpdateSettings` on it, that the XAML loads and tries to bind to
`_allTasks`, which _hadn't been constructed yet_
   * closes #17793
2024-08-23 12:21:44 -07:00
Leonard Hecker
47d9a87a23 Make fire_and_forget exception safe (#17783)
This PR clones `winrt::fire_and_forget` and replaces the uncaught
exception handler with one that logs instead of terminating.
My hope is that this removes one source of random crashes.

## Validation Steps Performed
I added a `THROW_HR` to `TermControl::UpdateControlSettings`
before and after the suspension point and ensured the application
won't crash anymore.
2024-08-23 12:19:42 -07:00
Leonard Hecker
b07589e7a8 Improve reliability of VT responses (#17786)
* Repurposes `_sendInputToConnection` to send output to the connection
  no matter whether the terminal is read-only or not.
  Now `SendInput` is the function responsible for the UI handling.
* Buffers responses in a VT string into a single string
  before sending it as a response all at once.

This reduces the chances for the UI thread to insert cursor positions
and similar into the input pipe, because we're not constantly unlocking
the terminal lock anymore for every response. The only way now that
unrelated inputs are inserted into the input pipe is because the VT
requests (e.g. DA1, DSR, etc.) are broken up across >1 reads.

This also fixes VT responses in read-only panes.

Closes #17775

## Validation Steps Performed
* Repeatedly run `echo ^[[c` in cmd.
  DA1 responses don't stack & always stay the same 
* Run nvim in WSL. Doesn't deadlock when pasting 1MB. 
* Run the repro from #17775, which requests a ton of OSC 4
  (color palette) responses. Jiggle the cursor on top of the window.
  Responses never get split up. 
2024-08-23 10:54:27 -07:00
Mike Griese
7d790c7c61 Prevent a crash when holding enter when creating a tab (#17788)
I guess I didn't realize that `SendCharEvent` could get called before `Create`. In that scenario, `enter` would hit the automark codepath (due to #17761), then crash because there was no text buffer.

Pretty easy to prevent.

Closes #17776
2024-08-23 10:49:11 -07:00
Mike Griese
9ec8584f86 Don't emit raw escape sequences in the test log output (#17790)
I used this very bad regex to try and find all the `\x1b`'s in ScreenBufferTests that weren't in a ProcessString call:
```
(?<!ProcessString.*)\x1b\[
```

And these looked like the ones that were the only violations. 

Closes #17736
2024-08-23 10:48:38 -07:00
Carlos Zamora
dbbc581154 Use WinGet API to improve Quick Fix results (#17614)
## Summary of the Pull Request
Improves Quick Fix's suggestions to use WinGet API and actually query
winget for packages based on the missing command.

To interact with the WinGet API, we need the
`Microsoft.WindowsPackageManager.ComInterop` NuGet package.
`Microsoft.WindowsPackageManager.ComInterop.Additional.targets` is used
to copy over the winmd into CascadiaPackage. The build variable
`TerminalWinGetInterop` is used to import the package properly.

`WindowsPackageManagerFactory` is used as a centralized way to generate
the winget objects. Long-term, we may need to do manual activation for
elevated sessions, which this class can easily be extended to support.
In the meantime, we'll just use the normal `winrt::create_instance` on
all sessions.

In `TerminalPage`, we conduct the search asynchronously when a missing
command was found. Search results are limited to 20 packages. We try to
retrieve packages with the following filters set, then fallback into the
next step:
1. `PackageMatchField::Command`,
`PackageFieldMatchOption::StartsWithCaseInsensitive`
2. `PackageMatchField::Name`,
`PackageFieldMatchOption::ContainsCaseInsensitive`
3. `PackageMatchField::Moniker`,
`PackageFieldMatchOption::ContainsCaseInsensitive`

This aligns with the Microsoft.WinGet.CommandNotFound PowerShell module
([link to relevant
code](9bc83617b9/src/WinGetCommandNotFoundFeedbackPredictor.cs (L165-L202))).

Closes #17378
Closes #17631
Support for elevated sessions tracked in #17677

## References
-
https://github.com/microsoft/winget-cli/blob/master/src/Microsoft.Management.Deployment/PackageManager.idl:
winget object documentation

## Validation Steps Performed
- [X] unelevated sessions --> winget query performed and presented
- [X] elevated sessions --> nothing happens (got rid of `winget install
{}` suggestion)
2024-08-23 19:20:29 +02:00
Mike Griese
1de142b4b1 Dismiss the snippet preview when there is no suggestions (#17777)
Pretty obvious in retrospect. If there's no results, then we need to
preview
_nothing_ to make sure that we clear out any old previews.

Closes #17773


Additionally, while I was here:

I realized why it seems like the selected item is so wacky when you
first open the sxnui:
* on launch we're not scrolling to the bottom item (which makes it
awkward in bottom up mode)
* when we filter the list, we're maintaining the selection _index_, not
the selection _item_.

Alas, that second part is... shockingly bodgy.
2024-08-23 10:12:00 -07:00
Dustin L. Howett
d1a1f9836e Restore off-the-top behavior for VT mouse mode (#17779)
PR #10642 and #11290 introduced an adjustment for the cursor position
used to generate VT mouse mode events.

One of the decisions made in those PRs was to only send coordinates
where Y was >= 0, so if you were off the top of the screen you wouldn't
get any events. However, terminal emulators are expected to send
_clamped_ events when the mouse is off the screen. This decision broke
clamping Y to 0 when the mouse was above the screen.

The other decision was to only adjust the Y coordinate if the core's
`ScrollOffset` was greater than 0. It turns out that `ScrollOffset` _is
0_ when you are scrolled all the way back in teh buffer. With this
check, we would clamp coordinates properly _until the top line of the
scrollback was visible_, at which point we would send those coordinates
over directly. This resulted in the same weird behavior as observed in
#10190.

I've fixed both of those things. Core is expected to receive negative
coordinates and clamp them to the viewport. ScrollOffset should never be
below 0, as it refers to the top visible buffer line.

In addition to that, #17744 uncovered that we were allowing
autoscrolling to happen even when VT mouse events were being generated.
I added a way for `ControlInteractivity` to halt further event
processing. It's crude.

Refs #10190
Closes #17744
2024-08-23 16:43:39 +00:00
Dustin L. Howett
e006f75f6c During Alt+Numpad composition, stash keys in case we bail out (#17774)
We were erroneously eating Alt followed by VK_ADD. This change makes
sure we cache key presses and releases that happen once a numpad
composition is active so that we can send them when you release Alt.

Right now, we only send them when you release Alt after composing Alt
and VK_ADD (entering hex mode) and only if you haven't inserted an
actual hex numpad code. This does mean that `Alt VK_ADD 0 0 H I` will
result in an input of "+hi". That... seems like a small price to pay for
Alt VK_ADD working again.

Closes #17762
2024-08-23 10:13:50 -05:00
Leonard Hecker
6dd9c468eb ConPTY: Flush unhandled sequences to the buffer (#17741)
#17510 made it so that VT requests like DA1 are passed through to the
hosting terminal and so conhost stopped responding to them on its own.
But since our input parser doesn't support proper passthrough (yet),
it swallowed the response coming from the terminal.

To solve this issue, this PR repurposes the existing boolean return
values to indicate to the parser whether the current sequence should
be flushed to the dispatcher as-is. The output parser always returns
true (success) and leaves its pass-through handler empty, while the
input parser returns false for sequences it doesn't expect.

## Validation Steps Performed
* Launch cmd
* Press `Ctrl+[`, `[`, `c`, `Enter` (= `^[[c` = DA1 request)
* DA1 response is visible 
2024-08-22 15:52:09 -07:00
Dustin L. Howett
cbb4a0a01c Allow OSC 17 to set the selection background color (#17742)
This pull request adds support for setting and querying the selection
color with `OSC 17`.

To make this possible, I had to move selection color down into the color
table where it always belonged. This lets us get rid of the special
`SetSelectionColor` method from the surface of AtlasEngine, and reunites
selection colors with the rest of the special colors.
2024-08-22 12:58:11 -05:00
Dustin L. Howett
eabebc4cb2 Guard Control UpdateAppearance/Settings against UAF (#17770)
When you close a window, it naturally loses focus.

We were trying to use members of the control to update its appearance on
focus loss after it got torn down.

Closes #17520
2024-08-22 10:55:49 -07:00
Leonard Hecker
b3f41626b4 ConPTY: Raise the MAX_PATH limit (#17768)
Swapped the `swprintf_s` with no failure checks against a
`str_printf_nothrow` with checks. I also deduplicated the
`CreateProcess` calls since they're mostly identical.

Closes #16860
2024-08-22 09:32:11 -07:00
Carlos Zamora
56cfb77c6d Log action dispatch occurrence (#17718)
Some simple logic to report whenever an action has successfully occurred
(and what ShortcutAction was used).

Note, there will be some false positives here from startup. I noticed we
get a `newTab` on launch. This is probably a result of restoring the
window layout of the previous session since we're using ActionAndArgs
for that.
2024-08-22 01:29:32 +02:00
Dustin L. Howett
628e99f5d2 Disable PGO for Release builds (#17765)
Same justification as #17749.

We will revert this when either OneBranch Custom Pools become
fit-for-purpose or they upgrade to VS 17.11. Or the heat death of the
universe.
2024-08-21 16:21:05 -07:00
Dustin L. Howett
3b4ee83ed1 Add support for querying Xterm dynamic colors and palette (#17729)
This pull request adds support for querying all of the "dynamic
resource" colors (foreground, background, cursor) as well as the entire
color palette using OSC 4, 10, 11 and 12 with the `?` color specifier.

To ease integration and to make it easier to extend later, I have
consolidated `SetDefaultForeground`, `SetDefaultBackground` and
`SetCursorColor` into one function `SetXtermColorResource`, plus its
analog `RequestXtermColorResource`.

Those functions will map xterm resource OSC numbers to color table
entries and optionally color _alias_ entries using a constant table. The
alias mappings are required to support reassigning the default
foreground and background to their indexed entries after a `DECAC`.

While there are only three real entries in the mapping table right now,
I have designs on bringing in selection background (xterm "highlight")
and foreground (xterm "highlightText").

We can also extend this to support resetting via OSC 110-119. However,
at the adapter layer we do not have the requisite information to restore
any of the colors (even the cursor color!) to the user's defaults.

`OSC 10` and `OSC 11` queries report the final values of
`DECAC`-reassigned entries, under the assumption that an application
asking for them wants to make a determination regardless of their
internal meaning to us (that is: they read through the aliased color to
its final destination in the color palette.)

I've tested this with lsix, which detects the background color before
generating sixel previews. It works great!

ConPTY does not currently pass OSC sequences received on the input
handle, so work was required to make it do so.

Closes #3718
2024-08-21 17:45:14 -05:00
PankajBhojwani
ce92b18507 Fix overwrite key binding warning in the SUI (#17763)
Fixes a regression from the actions MVVM change in #14292 - attempting
to overwrite a keybinding was displaying a warning but propagating the
change before the user acknowledged it.

The overwrite key binding warning in the SUI works like before

Closes #17754
2024-08-21 22:43:20 +00:00
Mike Griese
fd1b1c35b4 Set the default for autoMarkPrompts to true (#17761)
As we discussed in bug bash.

There's really no downside to us enabling it by default (and leaving
showMarksOnScrollbar: false). It'll mark lines as "prompts" when the
user hits enter. This will have a couple good side effects:
* When folks have right-aligned prompts (like, from oh-my-posh), the
`enter` will terminate where shell integration thinks the command is, so
that the right-prompt doesn't end up in the commandline history
* the scrollToMark actions will Just Work without any other shell
integration

Closes #17632
2024-08-21 17:07:06 -05:00
Mike Griese
5ff8b80358 x-save the commandline in the current window (#17758)
By manually setting the `_windowTarget` to `0`, we can make sure to toss
`x-save` commandlines at the current terminal window (so long as there
is one).

Edge cases:
* You passed other subcommands with `x-save`: Well, we'll do whatever we
would have normally done for multiple subcommands. We won't `x-save` in
the current window, we'll obey your settings. That seems to make sense
* You ran `wt x-save` without an open Terminal window: We'll open a
terminal window during the process of handling it. That seems sensible.

Closes #17366
2024-08-21 16:53:39 -05:00
Carlos Zamora
0a7c2585a2 Add ellipses back in to some command names (#17715)
In #16886, the key for the nested action got renamed from `Split
Pane...` to `Split pane`. This accidentally caused a collision because
now there's two actions with the same name! The settings model then
prefers the user's action over the one defined in defaults.json, thus
completely hiding the nested version.

I tried to balance the stylistic recommendations from #16846 (mainly
[this
comment](https://github.com/microsoft/terminal/issues/16846#issuecomment-2005007519)
since it gave some excellent examples) while trying to maintain muscle
memory as much as possible (with similar substring sequences). There was
also one case where we still used "the tab" so I removed the "the" for
consistency.

Side effect of #16886 which closed #16846
Closes #17294
Closes #17684
2024-08-21 20:13:39 +02:00
Leonard Hecker
a40a4ea094 ConPTY: Inject W32IM sequences (#17757)
Does what it says on the tin.

Part of #17737

## Validation Steps Performed
* In WSL run
  `printf "\e[?9001h"; sleep 1; printf "\e[?9001l"; read`
* Wait 1s and press Enter
* Run `showkey -a`
* Esc works 
2024-08-21 18:31:32 +02:00
Dustin L. Howett
1cb3445834 Force selection FG to black or white depending on BG luminance (#17753) 2024-08-21 07:50:04 -05:00
Dustin L. Howett
516ade54cb Port ServicingPipeline.ps1 to ProjectsV2 (#17756) 2024-08-21 07:49:36 -05:00
Leonard Hecker
056af83994 Fix pane event handlers being unbound (#17750)
I don't know what has changed between #17450 and now, but that fix
doesn't seem necessary anymore. If you add this action:
```json
{
    "keys": "ctrl+a",
    "command":
    {
        "action": "splitPane",
        "commandline": "cmd /c exit"
    }
}
```

and repeatedly spam Ctrl-A it used to lead to crashes. That doesn't
happen anymore, because some other PR must've fixed that.

Reverting #17450 fixes the issue found in #17578: Because the content
pointer didn't get reset to null anymore it meant that the root
pane retained the pointer after a split. After closing the split off
pane, it would assign the remaining one back to the root, which would
cause the still existing content pointer to be closed. That pointer
is potentially the same as the remaining pane and so no close events
would get received anymore.

Closes #17578

## Validation Steps Performed
* Add the above action and spam it 
* Start with an empty window, split pane, type `exit` in the new pane
  then type it in the original pane. It closes the window 
2024-08-21 14:44:42 +02:00
Carlos Zamora
7b39d24913 Fix UIA RangeFromPoint API (#17695)
## Summary of the Pull Request
Fixes the `RangeFromPoint` API such that we're now properly locking when
we attempt to retrieve the viewport data. This also corrects the
conversion from `UiaPoint` (screen position) to buffer coordinates
(buffer cell).

Closes #17579 

## Detailed Description of the Pull Request / Additional comments
- `UiaTextRangeBase::Initialize(UiaPoint)`:
- reordered logic to clamp to client area first, then begin conversion
to buffer coordinates
   - properly lock when retrieving the viewport data
- updated `_TranslatePointToScreen` and `_TranslatePointFromScreen` to
use `&` instead of `*`
   - we weren't properly updating the parameter before
- `TermControlUiaTextRange::_TranslatePointFromScreen()`
- `includeOffsets` was basically copied over from
`_TranslatePointToScreen`. The math itself was straight up wrong since
we had to do it backwards.

## Validation Steps Performed
 Moved WT to top-left of monitor, then used inspect.exe to call
`RangeFromPoint` API when mouse cursor is on top-left buffer cell (also
meticulously stepped through the two functions ensuring everything was
correct).
2024-08-20 15:58:05 -07:00
Leonard Hecker
249fe2aca1 Fix a crash when disabling the ASB (#17748)
`ProcessString` may delete the ASB and cause a dangling screen info
pointer. As such, we must avoid using the pointer after the call.

Closes #17709

## Validation Steps Performed
I couldn't repro the issue.
2024-08-20 21:58:54 +02:00
Dustin L. Howett
408f3e2bfd Disable PGO for nightly builds (#17749)
Refs #17699
2024-08-20 14:15:27 -05:00
Leonard Hecker
e0dae59f38 Fix a crash during settings update (#17751)
* Adds a check whether the thread dispatcher is already null.
  (See code comments.)
* Moves the `_settings` to only happen on the UI thread.
  Anything else wouldn't be thread safe.

Closes #17620

## Validation Steps Performed
Not reproducible. 🚫
2024-08-20 20:21:21 +02:00
Mike Griese
60ac45c239 Log when WindowsTerminal.exe starts (#17745)
as discussed in team sync
2024-08-20 10:18:42 -05:00
Leonard Hecker
b439925acc Fix session persistence when the session ends (#17714)
Once all applications that have received a `WM_ENDSESSION` message
have returned from processing said message, windows will terminate
all processes. This forces us to process the message synchronously.
This meant that this issue was timing dependent. If Windows Terminal
was quick at persisting buffers and you had some other application that
was slow to shut down (e.g. Steam), you would never see this issue.

Closes #17179
Closes #17250

## Validation Steps Performed
* Set up a lean Hyper-V VM for fast reboots
* `Set-VMComPort <vm> 1 \\.pipe\\<pipe>`
* Hook up WIL to write to COM1
* Add a ton of debug prints all over the place
* Read COM output with Putty for hours
* RTFM, and notice that the `WM_ENDSESSION` documentation states
  "the session can end any time after all applications
  have returned from processing this message"
* Be very very sad 
* Fix it
* Rebooting now shows on COM1 that persistence runs 
* Windows get restored after reboot 
2024-08-20 13:24:51 +02:00
James Holderness
131728b17d Fix input sequences split across the buffer boundary (#17738)
## Summary of the Pull Request

When conhost receives input from a conpty connection, and that input
arrives in a block larger than our 4K buffer, we can end up with a VT
sequence that's split at the buffer boundary. Previously that could
result in the start of the sequence being dropped, and the remaining
characters being interpreted as individual key presses.

This PR attempts to fix the issue by caching the unprocessed characters
from the start of the sequence, and then combining them with the second
half of the sequence when it's later received.

## Validation Steps Performed

I've confirmed that pasting into vim now works correctly with the sample
data from issue #16655. I've also tested with a `DECCTR` report larger
than 4K which would previously have been corrupted, and which now works
as expected.

## PR Checklist
- [x] Closes #16655
2024-08-19 19:22:56 -05:00
Windows Console Service Bot
37e2bc0caa Localize PDP changelogs - main - 08/16/2024 23:36:05 (#17732) 2024-08-19 13:52:54 -05:00
Dustin L. Howett
735ef2823e Restore the ability to disable checking for URLs (#17731)
Fixes #17727
2024-08-19 10:44:26 -05:00
Dustin L. Howett
faf21acbc7 atlas: draw selection colors as background/foreground instead of alpha overlay (#17725)
With the merge of #17638, selections are now accumulated early in the
rendering process. This allows Atlas, which currently makes decisions
about cell foreground/background at the time of text rendering,
awareness of the selection ranges *before* text rendering begins.

As a result, we can now paint the selection into the background and
foreground bitmaps. We no longer need to overlay a rectangle, or series
of rectangles, on top of the rendering surface and alpha blend the
selection color onto the final image.

As a reminder, "alpha selection" was always a stopgap because we didn't
have durable per-cell foreground and background customization in the
original DxEngine.

Selection foregrounds are not customizable, and will be chosen using the
same color distancing algorithm as the cursor. We can make them
customizable "easily" (once we figure out the schema for it) for #3580.

`ATLAS_DEBUG_SHOW_DIRTY` was using the `Selection` shading type to draw
colored regions. I didn't want to break that, so I elected to rename the
`Selection` shading type to `FilledRect` and keep its value. It helps
that the shader didn't have any special treatment for
`SHADING_TYPE_SELECTION`.

This fixes the entire category of issues created by selection being an
80%-opacity white rectangle. However, given that it changes the imputed
colors of the text it will reveal `SGR 8` concealed/hidden characters.

Refs #17355
Refs #14859
Refs #11181
Refs #8716
Refs #4971
Closes #3561
2024-08-19 14:54:18 +00:00
Dustin L. Howett
9b21b78fee pdp: add quick release notes to the store pages (#17730)
Now that the store displays changelogs, it seems unfair for us to not
put something in here.

These are intended to give a rough idea, not to be perfect, as they are
not the product of my hours of changelog writing (since I am lazy and
put that off until the day of release 🫣)
2024-08-16 18:25:40 -05:00
Dustin L. Howett
4c018efd64 chore: Update to TAEF 10.93.240607003 (#16595) 2024-08-16 22:50:19 +00:00
Dustin L. Howett
1ef497970f Introduce the concept of "selection spans" instead of "rects" (#17638) 2024-08-15 14:00:40 -05:00
James Holderness
65219d40ce Fix misalignment of Sixel image slices (#17724)
When we have a series of image slices of differing widths, which also
don't align with the cell boundaries, we can get rounding errors in the
scaling which makes the different slices appear misaligned.

This PR fixes the issue by removing the 4 pixel width alignment that was
enforced in the `ImageSlice` class, since that's not actually necessary
when the pixels themselves are already 4 bytes in size. And without
that, the widths should be correctly aligned with the cell boundaries.

## References and Relevant Issues

The initial Sixel implementation was added in PR #17421.

## Validation Steps Performed

I've confirmed that this fixes the rendering glitches reported in
#17711, and all my existing Sixel tests still work as expected.

Closes #17711
2024-08-15 09:39:28 -07:00
Leonard Hecker
bf44b6c360 Fix a misdiagnosis in MSVC 17.11 (#17723)
Mo' compiler, mo' problems.
2024-08-15 09:33:43 -07:00
Carlos Zamora
1511d2c2ad schema: add reloadEnvironmentVariables to newTerminalArgs (#17696) 2024-08-14 15:16:04 -05:00
Leonard Hecker
7fd9c5c789 Align the OSS ConPTY API with Windows 11 24H2 (#17704) 2024-08-14 15:15:50 -05:00
Leonard Hecker
9c1436775e Use a plain char array to pass connection input (#17710)
`HSTRING` does not permit strings that aren't null-terminated.
As such we'll simply use a plain char array which compiles down to
a `UINT32` and `wchar_t*` pointer pair. Unfortunately, cppwinrt uses
`char16_t` in place of `wchar_t`, and also offers no trivial conversion
between `winrt::array_view` and `std::wstring_view` either.
As such, most of this PR is about explicit type casting.

Closes #17697

## Validation Steps Performed
* Patch the `DeviceAttributes` implementation in `adaptDispatch.cpp`
  to respond like this:
   ```cpp
   _api.ReturnResponse({L"ABCD", 3});
   ```
* Open a WSL shell and execute this:
  ```sh
  printf "\e[c"; read
  ```
* Doesn't crash 
2024-08-13 21:35:47 -05:00
James Holderness
4a40c4329a Add support for querying the DECCTR color table report (#17708)
This PR introduces the framework for the `DECRQTSR` sequence which is
used to query terminal state reports. But for now I've just implemented
the `DECCTR` color table report, which provides a way for applications
to query the terminal's color scheme.

## References and Relevant Issues

This is the counterpart to the the `DECRSTS` sequence, which is used to
restore a color table report. That was implemented in PR #13139, but it
only became practical to report the color table once conpty passthrough
was added in PR #17510.

## Detailed Description of the Pull Request / Additional comments

This sequence has the option of reporting the colors as either HLS or
RGB, but in both cases the resolution is lower than 24 bits, so the
colors won't necessarily round-trip exactly when saving and restoring.
The HLS model in particular can accumulate rounding errors over time.

## Validation Steps Performed

I've added a basic unit test that confirms the colors are reported as
expected for both color models. The color values in these tests were
obtained from color reports on a real VT525 terminal.

## PR Checklist
- [x] Tests added/passed
2024-08-13 18:53:26 -05:00
Dustin L. Howett
06c07ab50d Switch to the new and beautiful VS Dev Shell icons (#17706)
We got some new icons for Developer Command Prompt and Developer
PowerShell from our friends over on Visual Studio!

This pull request includes them in the package, and fixes up the VS
dynamic profiles to reset any icons that matched the old paths.

This may be a minor breaking change for user settings, but we're making
the assumption that if they didn't change their VS profile icons from
the defaults, they probably want to follow us to the new defaults.

To prevent anything like this from happening again, we're going to stop
serializing icons for stub profiles.

I've also included a VS version of the PowerShell "black" icon which is
currently unused, but can be used in the future for PS7+-based VS Dev
Shell.

Closes #17627
2024-08-13 18:21:09 -05:00
Leonard Hecker
0fd8dc575f Remove CHAR_INFO munging for raster fonts (#17681)
`RealUnicodeToFalseUnicode` was described as:
> This routine converts a unicode string into the correct characters
> for an OEM (cp 437) font. This code is needed because the gdi glyph
> mapper converts unicode to ansi using codepage 1252 to index font.
> This is how the data is stored internally.

In other words, it takes a UCS2 string, translates it to the current
codepage and translates it back to UCS2 in the US version of Windows.
In the "eastern" DBCS version it "reinterprets" the DBCS string as
`CP_USA` (a particularly weird quirk).

The original implementation used to do this translation at every
opportunity where text went into or out of conhost.
The translation was weird, but it was consistent.
In Windows 10 RS1 conhost got a new UCS2-aware text buffer and
this translation was removed from most places, as the text buffer
was converted to store proper UCS2. This broke the entire concept
of the translation though. Whatever data you previously wrote with
something like `WriteConsoleOutputCharacter` now came back with
something entirely else via `ReadConsoleOutput`.

In other words, I believe past RS1 there was technically never any
point in "munging" `CHAR_INFO`s, as this only covered 2 API functions.

Still, this does mean that this PR represents an API breaking change.
It's a minor one though, because it only affects 2 API functions.
And more importantly, it's a necessary breaking change as we move
further and further away from correlating codepoint and column counts.

## Validation Steps Performed
* Remaining tests pass 
2024-08-13 18:18:16 -05:00
Leonard Hecker
9074e9d6a8 Fix session restoration of full buffers (#17654)
This removes the `Terminal::SetViewportPosition` call from session
restoration which was responsible for putting the viewport below
the buffer height and caused the renderer to fail.

In order to prevent such issues in the future, `SetViewportPosition`
now protects itself against out of bounds requests.

Closes #17639

## Validation Steps Performed
* Enable persistence
* Print `big.txt`
* Restart
* Looks good 
2024-08-13 18:17:17 -05:00
Dustin L. Howett
2478c643f4 Fix PowerShell profile warnings during WT build (oops) (#17705) 2024-08-13 11:12:30 -05:00
Dustin L. Howett
0199ca33dd Port selection in conhost and Terminal to use til::generational (#17676)
In #17638, I am moving selection to an earlier phase of rendering (so
that further phases can take it into account). Since I am drafting off
the design of search highlights, one of the required changes is moving
to passing `span`s of `point_span`s around to make selection effectively
zero-copy.

We can't easily have zero-copy selection propagation without caching,
and we can't have caching without mandatory cache invalidation.

This pull request moves both conhost and Terminal to use
`til::generational` for all selection members that impact the ranges
that would be produced from `GetSelectionRects`.

This required a move from `std::optional<>` to a boolean to determine
whether a selection was active in Terminal.

We will no longer regenerate the selection rects from the selection
anchors plus the text buffer *every single frame*.

Apart from being annoying to read, there is one downside.

If you begin a selection on a narrow character, _and that narrow
character later turns into a wide character_, we will show it as
half-selected.

This should be a rare-enough case that we can accept it as a regression.
2024-08-09 13:11:28 -05:00
Leonard Hecker
7c0d6d95db Slim down shell extension and elevate-shim (#15327)
This simplifies the code (from the perspective of the CPU) by doing
some miniscule-feels-good optimizations like replacing `snprintf` with
regular string concatenation and by doing an actual optimization by
removing the remaining calls to the WinRT `ApplicationModel` namespace.

More importantly however it fixes a bug: The only reason `elevate-shim`
worked at all is because the shell extension passed "wrong" parameters
to `CreateProcess`. Instead of repeating the application path in the
command line argument again, as is convention in C and on Windows, and
getting the 2nd and following parameters as an argument to `wWinMain`,
it used `GetCommandLineW` to get the original, broken command line.
This fixes the issue by passing the application path as the first
argument, which allows `elevate-shim` to be called like any other app.

## Validation Steps Performed
* Deploy WT and restart explorer
* Clicking "Open in Terminal (Dev)" works 
* Clicking "Open in Terminal (Dev)" while holding Ctrl+Shift
  opens WT as admin 
2024-08-09 15:33:12 +00:00
James Holderness
edfa3ea0f0 Remove SetTextAttributes from the ITerminalApi interface (#17685)
The only reason we had the `SetTextAttributes` method in `ITerminalApi`
was to allow for conhost to remap the default color attributes when the
VT PowerShell quirk was active. Since that quirk has now been removed,
there's no need for this API anymore.

## References and Relevant Issues

The PowerShell quirk was removed in PR #17666.

## Validation Steps Performed

I've had to update all the attribute tests in adapterTest to manually
check the expected attributes, since those checks were previously being
handled in a `SetTextAttributes` mock which no longer exists.

I've also performed some manual tests of the VT attribute operations to
double check that they're still working as expected.
2024-08-08 18:45:16 -05:00
Leonard Hecker
9ab2870bc3 Upgrade fmt to 11.0.2 (#16007)
Between fmt 7.1.3 and 11.0.2 a lot has happened. `wchar_t` support is
now more limited and implicit conversions don't work anymore.

Furthermore, even the non-`FMT_COMPILE` API is now compile-time checked
and so it fails to work in our UI code which passes `hstring` format
strings which aren't implicitly convertible to the expected type.
`fmt::runtime` was introduced for this but it also fails to work for
`hstring` parameters. To solve this, a new `RS_fmt` macro was added
to abstract the added `std::wstring_view` casting away.

Finally, some additional changes to reduce `stringstream` usage
have been made, whenever `format_to`, etc., is available.
This mostly affects `ActionArgs.cpp`.

Closes #16000

## Validation Steps Performed
* Compiles 
* Settings page opens 
2024-08-08 15:40:05 -07:00
Carlos Zamora
ac865e6666 Log number of interactive sessions (#17682)
This sends a telemetry event if a session is interacted with.
Specifically, key events are essential to have an interactive session in
Windows Terminal, so we're tracking sessions that have had a key down
event.
2024-08-08 15:52:07 +02:00
James Holderness
746cf1f148 Add support for the VT answerback capability (#17660)
The answerback feature allows for the user to define a message that the
terminal will transmit to the host whenever an `ENQ` (enquiry) control
character is received.

## Detailed Description of the Pull Request / Additional comments

In Windows Terminal, the message can be configured at the profile level
of the settings file, as a string property named `AnswerbackMessage`.

In ConHost, the message can be configured in the registry, again as a
string value with the name `AnswerbackMessage`.

## Validation Steps Performed

I've confirmed that the control is working as intended in both Windows
Terminal and ConHost using Vttest.

Closes #11946
2024-08-07 17:46:01 -05:00
Leonard Hecker
2c452e0fd6 Remove IsGlyphFullWidth from InputBuffer (#17680)
In several places the old conhost codebase appears to assume that any
wide glyph is represented by two codepoints. This is probably an
artifact of the ASCII/DBCS split that conhost used to have.
When conhost got merged into a single UCS2-aware application,
this artifact was apparently never properly resolved.

To my knowledge there are at least two places where this assumption
exists: The clipboard code which translates non-wide non-ascii
characters to Alt-numpad sequences, and this code. Both are wrong.
This is because in a Unicode-context there's no correlation between
the number of codepoints and the width of the glyph, even with UCS2.

In a post-UCS2-world the correct check is for surrogate pairs,
as they must be avoided for the same reason DBCS were avoided.

One could consider this a breaking change of the API,
as this can now result in repeat counts >1 for wide glyphs.
If someone complained about this change in behavior, I'd probably
not change it back, as narrow complex Unicode characters exist too.
2024-08-07 22:19:11 +02:00
Leonard Hecker
d4c1dad0fe AtlasEngine: Fix curly line rendering on rows with line renditions (#17679)
This fixes several scaling issues with double width/height rows.

## Validation Steps Performed
* Run `RenderTests` 
2024-08-07 22:19:01 +02:00
Leonard Hecker
9d0180a554 Fix cooked read reflow under ConPTY (#17668)
This delays the CSI J until we know the new origin of the prompt.
That way it's at the right (reflowed) position.

## Validation Steps Performed
* conhost
  * Print a ton of text
  * Write a prompt of a hundred chars
  * Resize the window very narrow / wide
  * Works 
* Windows Terminal
  * Write a prompt of a hundred chars
  * Resize the window very narrow / wide
  * Works 
2024-08-07 12:58:10 -05:00
Leonard Hecker
2fab9866b2 Implement Alt-Numpad handling (#17637)
This adds an indirection for `_KeyHandler` so that `OnDirectKeyEvent`
can call `_KeyHandler`. This allows us to consistently handle
Alt-key-up events. Then I added custom handling for Alt+ddd (OEM),
Alt+0ddd (ANSI), and Alt+'+'+xxxx (Unicode) sequences, due to the
absence of Alt-key events with xaml islands and our TSF control.

Closes #17327

## Validation Steps Performed
* Tested it according to https://conemu.github.io/en/AltNumpad.html
* Unbind Alt+Space
* Run `showkey -a`
* Alt+Space generates `^[ `
* F7 generates `^[[18~`
2024-08-07 09:32:31 +02:00
e82eric
0bafab9a0f Avoid covering current search highlight with search box (#17516)
## Summary of the Pull Request
Adds a scroll offset to avoid hiding the current search highlight with
the search box.
- Offset is based on the number of rows that the search box takes up.
  (I am not totally sure I am calculating this right)
- This won't help when the current highlight is in the first couple
  rows of the buffer.

Fixes: #4407
2024-08-07 08:32:16 +02:00
Leonard Hecker
f6a415511a Address feedback from #17510 (#17645)
* Added/changed comments as mentioned.
* Improved the ugly `resize_and_overwrite` hack into the STL.
* Add `Write` functions for xterm's window API.
* The only reason we needed a move operator for `VtIo::Writer`
  is because we used it in a ternary in `CONSOLE_INFORMATION`.
  Ternaries are like if branches with hidden move assignments.
  Instead, we simply construct each `Writer` in place.
  No ternary = No move = No problems in life.
  The best benefit of this is that this makes calling `GetVtWriter`
  a hundred times cheaper.

Otherwise, I still need to extend a few tests in `VtIoTests`,
but I'm planning to do that later.
2024-08-07 08:23:17 +02:00
Leonard Hecker
7d8455d4eb Be less strict about trivialities (#17646)
As explained in the updated code comment, we can actually safely
allow more types to enjoy the fast-pass in `til::small_vector`.
2024-08-07 08:23:06 +02:00
Leonard Hecker
dd5f2ad755 Return strings directly from read_file_as_utf8_string_if_exists (#17667)
* Every single place that called `read_file_as_utf8_string_if_exists`
  would immediately do a `.value_or(std::string{})`.
  As such, the function now returns a string directly.
* There was just one caller to `read_file_as_utf8_string`
  and it only cared about files that are non-empty.
  As such, the specialization got removed.

Both of these make sense to me, as in practice there's seldom
a difference between an empty file and a non-existent one.

## Validation Steps Performed
* Compiles 
* Starts 
* Deleting the `settings.json` contents triggers a reload 
2024-08-06 23:53:47 +02:00
Dustin L. Howett
9a0d784500 til: add til::point_span_subspan_within_rect (#17675)
This pulls one of the inlines in AtlasEngine out as a helper so we can
use it elsewhere.
2024-08-06 23:53:38 +02:00
Leonard Hecker
24450a3dd7 Update scratch project dependencies (#17648)
This simply makes the project compile again.
2024-08-06 23:53:24 +02:00
Dustin L. Howett
9007fc2894 atlas,d2d: overdraw background bitmap by one cell on all sides (#17674)
BackendD2D will now draw one extra cell on all sides when rendering the
background, filled with the expected background color, starting at (-1,
-1) to ensure that cell backgrounds do not bleed over the edges of the
viewport where the is swapchain but no content.

Fixes #17672
2024-08-06 16:50:48 -05:00
Dustin L. Howett
07c7167535 Hygiene: get rid of all instances of hstring ctor'd with L"" (#17655)
One of these things can be optimized. It's not the one you thought.
2024-08-06 16:41:21 -05:00
Leonard Hecker
dfb52331f8 Remove VT color quirk for PowerShell (#17666)
Roughly 4 years ago we gave Windows Terminal the ability to
differentiate between black/white and the default colors.
One of the victims was PowerShell and most importantly PSReadLine,
which emit SRG 37 & 40 when what they really want is 38 & 48.
We fixed this on our side by adding a shim.

Since the addition of VT passthrough in #17510 we now intentionally
lost the ability to translate VT sequences from one thing to another.
This meant we also lost the ability to do this shim and as such
this PR removes it. Luckily Windows 11 now ships PSReadLine 2.0.0,
which contains a proper fix for this.

Unfortunately, this is not the case for Windows 10, which ships
PSReadLine 2.0.0-beta2. Users affected by this will have to install
a newer version of PSReadLine or use the default black/white theme.

See 1bf4c082b4

Closes #13037
2024-08-06 14:23:03 +02:00
James Holderness
5174c96d6d Add support for querying the DECSCUSR setting (#17659)
This PR adds support for querying the cursor style - technically the
state of the `DECSCUSR` setting - using a `DECRQSS` escape sequence.

## References and Relevant Issues

The initial `DECRQSS` support was added in PR #11152, but it wasn't
practical to report the cursor style until conpty passthrough was added
in PR #17510.

## Detailed Description of the Pull Request / Additional comments

If the user has chosen a cursor style that isn't one of the shapes
supported by the `DECSCUSR` control, we report those as 0 (i.e. the
default style). That way, if an application later tries to restore the
cursor using the returned value, it should still be reset to its
original state.

I also took the opportunity in this PR to do some refactoring of the
other `DECRQSS` reports, since several of them were using unnecessary
appending that could be simplified to a single `fmt::format` call, or
even just static strings in some cases.

## Validation Steps Performed

I've checked the reports are working as expected in Vttest, and also
added some unit tests.

## PR Checklist
- [x] Tests added/passed
2024-08-05 14:54:55 -07:00
Leonard Hecker
8149bd0dd0 wint_t is not wchar_t (#17653)
`towupper` return `wint_t` which is `int`. 🤦

## Validation Steps Performed
Open the settings menu. 🤦
2024-08-02 14:43:41 -05:00
Dustin Howett
8207f26bcc Reapply "Reapply "build: switch back to the "latest" OneBranch build image (#17630)""
This reverts commit c0774dcda8.
2024-08-02 13:56:12 -05:00
Dustin L. Howett
c0774dcda8 Revert "Reapply "build: switch back to the "latest" OneBranch build image (#17630)""
This reverts commit 114c2b44d1.
2024-08-02 12:09:49 -05:00
Dustin L. Howett
114c2b44d1 Reapply "build: switch back to the "latest" OneBranch build image (#17630)"
This reverts commit a8582978af.
2024-08-01 15:52:32 -05:00
Leonard Hecker
c7be9a2dbf Potential hotfix for a MSVC 14.40.33807 miscompilation (#17644)
We aren't sure what exactly it is, but on the latest toolchain
this code miscompiles. The fmt call throws an exception because
it supposedly has too few arguments supplied for the format string.
Debugging the issue shows that the `next_arg_id_` internal to `fmt`
is 10000, even though it's parsing the first argument. At that point
it's supposed to be 0. This code hasn't been changed in years.

My hope is that this slight shuffling of the code causes
the issue to go away.
2024-08-01 15:51:56 -05:00
Leonard Hecker
450eec48de A minor ConPTY refactoring: Goodbye VtEngine Edition (#17510)
The idea is that we can translate Console API calls directly to VT at
least as well as the current VtEngine setup can. For instance, a call
to `SetConsoleCursorPosition` clearly translates directly to a `CUP`
escape sequence. Effectively, instead of translating output
asynchronously in the renderer thread, we'll do it synchronously
right during the Console API call.

Most importantly, the this means that any VT output that an
application generates will now be given to the terminal unmodified.

Aside from reducing our project's complexity quite a bit and opening
the path towards various interesting work like sixels, Device Control
Strings, buffer snapshotting, synchronized updates, and more, it also
improves performance for mixed text output like enwik8.txt in conhost
to 1.3-2x and in Windows Terminal via ConPTY to roughly 20x.

This adds support for overlapped IO, because now that output cannot
be "skipped" anymore (VtEngine worked like a renderer after all)
it's become crucial to block conhost as little as possible.

⚠️ Intentionally unresolved changes/quirks:
* To force a delayed EOL wrap to wrap, `WriteCharsLegacy` emits a
  `\r\n` if necessary. This breaks text reflow on window resize.
  We cannot emit ` \r` the way readline does it, because this would
  overwrite the first column in the next row with a whitespace.
  The alternative is to read back the affected cell from the buffer
  and emit that character and its attributes followed by a `\r`.
  I chose to not do that, because buffer read-back is lossy (= UCS2).
  Unless the window is resized, the difference is unnoticeable
  and historically, conhost had no support for buffer reflow anyway.
* If `ENABLE_VIRTUAL_TERMINAL_PROCESSING` is set while
  `DISABLE_NEWLINE_AUTO_RETURN` is reset, we'll blindly replace all
  LF with CRLF. This may hypothetically break DCS sequences, but it's
  the only way to do this without parsing the given VT string and
  thus the only way we can achieve passthrough mode in the future.
* `ENABLE_WRAP_AT_EOL_OUTPUT` is translated to `DECAWM`.
  Between Windows XP and Windows 11 21H2, `ENABLE_WRAP_AT_EOL_OUTPUT`
  being reset would cause the cursor position to reset to wherever
  a write started, _if_ the write, including expanded control chars,
  was less than 100 characters long. If it was longer than that,
  the cursor position would end up in an effectively random position.
  After lengthy research I believe that this is a bug introduced in
  Windows XP and that the original intention was for this mode to be
  equivalent to `DECAWM`. This is compounded by MSDN's description
  (emphasis mine):
  > If this mode is disabled, the **last character** in the row is
  > overwritten with any subsequent characters.

⚠️ Unresolved issues/quirks:
* Focus/Unfocus events are injected into the output stream without
  checking whether the VT output is currently in a ground state.
  This may break whatever VT sequence is currently ongoing.
  This is an existing issue.
* `VtIo::Writer::WriteInfos` should properly verify the width of
  each individual character.
* Using `SetConsoleActiveScreenBuffer` destroys surrogate pairs
  and extended (VT) attributes. It could be translated to VT pages
  in the long term.
* Similarly, `ScrollConsoleScreenBuffer` results in the same and
  could be translated to `DECCRA` and `DECFRA` in the near term.
  This is important because otherwise `vim` output may loose
  its extended attributes during scrolling.
* Reflowing a long line until it wraps results in the cooked read
  prompt to be misaligned vertically.
* `SCREEN_INFORMATION::s_RemoveScreenBuffer` should trigger a
  buffer switch similar to `SetConsoleActiveScreenBuffer`.
* Translation of `COMMON_LVB_GRID_HORIZONTAL` to `SGR 53` was dropped
  and may be reintroduced alongside `UNDERSCORE` = `SGR 4`.
* Move the `OSC 0 ; P t BEL` sequence to `WriteWindowTitle`
  and swap the `BEL` with the `ST` (`ESC \`).
* PowerShell on Windows 10 ships with PSReadLine 2.0.0-beta2
  which emits SGR 37/40 instead of 39/49. This results in black
  spaces when typing and there's no good way to fix that.
* A test is missing that ensures that `FillConsoleOutputCharacterW`
  results in a `CSI n J` during the PowerShell shim.
* A test is missing that ensures that `PtySignal::ClearBuffer`
  does not result in any VT being generated.

Closes #262
Closes #1173
Closes #3016
Closes #4129
Closes #5228
Closes #8698
Closes #12336
Closes #15014
Closes #15888
Closes #16461
Closes #16911
Closes #17151
Closes #17313
2024-08-01 20:38:10 +00:00
Dustin L. Howett
a8582978af Revert "build: switch back to the "latest" OneBranch build image (#17630)"
This reverts commit 39108a7a1b.
2024-07-31 18:33:05 -05:00
Leonard Hecker
50fe0f82ce Fix sixels in BackendD2D (#17636)
Whoops.
2024-07-31 18:05:46 +02:00
Leonard Hecker
9bafa52c65 Fix alt-numpad events (#17635)
This fixes a regression caused by 5b44476 which accidentally moved
the two pushes into the if condition.

Closes MSFT:52463679

## Validation Steps Performed
* Enable `Feature_UseNumpadEventsForClipboardInput`
* `cmd`
* `chcp 54936`
* Paste narrow Unicode characters like ①
* It works 
2024-07-31 18:05:35 +02:00
Dustin L. Howett
39108a7a1b build: switch back to the "latest" OneBranch build image (#17630)
Thanks to a string of compiler bugs, we had to use an older container
image that shipped with VS 17.9.

Unfortunately, that container image is falling further and further out
of date. The build agents don't cache it any longer, so they spend 30-45
minutes of every build pulling it from the registry.

With the changes to ConPTY in #17510 removing the need for til::bitmap,
we no longer need to work around the compiler bugs it exposed.

Furthermore, 17.10.6+ has a much more robust and presumably "working"
compiler.
2024-07-31 01:26:28 +02:00
Leonard Hecker
d730cfda9f Add a spec for an In-process ConPTY (#17387)
👉 Preview:
https://github.com/microsoft/terminal/blob/dev/lhecker/13000-spec/doc/specs/%2313000%20-%20In-process%20ConPTY.md

The spec has a tl;dr! The tl;dr^2 for the commit message:
* Less bugs
* Less code
* More perf
2024-07-30 11:35:42 -05:00
Leonard Hecker
2f43886ab5 Fix colors getting lost on reflow (#17568)
The "copy the remaining attributes" loop assumes that it has full
ownership over the rows that it copies. For that to be true,
we have to of course make sure that the current write-cursor
is at a fresh, new row in the first place.

## Validation Steps Performed
* In a new pwsh tab with 120 colums:
  ``Write-Host -NoNewline "`e[36m$('a'*120)`e[m"; sleep 10``
* Resize the window wider
* Color doesn't get lost
2024-07-30 11:32:16 -05:00
Leonard Hecker
295cd17b02 Fix cursor invalidation, again (#17617)
Regressed in #15500, incorrectly fixed in #17332, exposed by #17583.
My ineptitude on full display. If this isn't the last cursor
invalidation bug I'm going to cry.

Closes #17615

## Validation Steps Performed
* cmd.exe
* a directory with 6 files
* 80x24 viewport
* run `cls`
* run `dir` twice
2024-07-26 20:09:25 +02:00
David Federman
54ef019a46 Update MSBuildCache to 0.1.283-preview (#17604)
Update MSBuildCache to 0.1.283-preview

Notable change is this one, which should avoid under-builds when the
build tooling updates: https://github.com/microsoft/MSBuildCache/pull/77

Full release notes:
[0.1.283-preview](https://github.com/microsoft/MSBuildCache/releases/tag/v0.1.283-preview)
2024-07-26 09:24:45 -07:00
Mike Griese
21fa303a3d Add support for local snippets in the CWD (#17388)
This PR adds the ability to load snippets from the CWD into the
suggestions UI.

If shell integration is disabled, then we only ever think the CWD for a
pane is it's `startingDirectory`. So, in the default case, users can
still stick snippets into the root of their git repos, and have the
Terminal load them automatically (for profiles starting in the root of
their repo).
If it's enabled though, we'll always try to load snippets from the CWD
of the shell.

* We cache the actions into a separate map of CWD -> actions. This lets
us read the file only the first time we see a dir.
* We clear that cache on settings reload
* We only load `sendInput` actions from the `.wt.json`

As spec'd in #17329
2024-07-25 20:39:26 -05:00
Dustin L. Howett
7851c96812 Make WindowsTerminal F5-runnable (#17577)
Dear god, what have I done.
2024-07-23 15:28:57 -07:00
Dustin L. Howett
e02d46bdd2 Update .vsconfig to include vcpkg and some newer tools (#17603)
Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
2024-07-23 15:28:04 -07:00
Leonard Hecker
75f7ae4bec AtlasEngine: Implement sixels (#17581)
* Add a revision to `ImageSlice` so that the renderers
  can use it to cache them as bitmaps across frames.
* Hooked up the revision tracking to AtlasEngine to cache the
  slices into `Buffer`s so we can own them into the `Present`.
* Hooked up those snapshots to BackendD3D with a straightforward
  hashmap -> atlas-rect logic. Just like rendering text.
* Hooked up BackendD2D with a bad, but simple & direct drawing logic.
* Bonus: Modify `ImageSlice` to be returned as a raw pointers
  as this helps performance slightly. (Trivial type == good.)
* Bonus: Fixed the `_debugShowDirty` code (disabled by default).

## Validation Steps Performed
* `mpv --really-quiet --vo=sixel foo.mp4` looks good 
* Scroll up down & observe dirty rects 
2024-07-23 12:39:12 -07:00
Dustin L. Howett
6372baa0d3 README: move build badges to shine-oss, remove colortool, move to top (#17589) 2024-07-22 10:30:38 -07:00
Dustin L. Howett
cbc8eed476 build: bootstrap vcpkg before nuget restore (#17592)
`nuget restore` actually runs through MSBuild! However, #15855 added a
dependency from our project on a system-installed _or locally detected_
`vcpkg.targets` (or `.props`).

Our build runs `nuget restore` before finding or installing vcpkg, so
the rules in our project file would try to import vcpkg before it had
been found (or installed).

On build agents with vcpkg installed via the VS workload, this was fine:
we would import the one that came with VS and go on our merry way. On
build agents where it needs to be installed locally, it could not be
imported.

The fix in this PR is to install/bootstrap vcpkg before running nuget.

I tried to isolate the vcpkg rules to only run _in the absence of
nuget_, but that didn't work.
2024-07-22 10:23:34 -07:00
Craig Loewen
d74440f0f1 Remove "SimilarIssues" Bot prototype (#17598)
Removes the GitHub action that provides the functionality for the
similar issues bot prototype. We can onboard to the more official
prototype instead to conserve functionality.
2024-07-22 10:10:06 -07:00
David Federman
65accfd5c3 MSBuildCache: Allow duplicate outputs under "obj\*\vcpkg\**" (#17597) 2024-07-22 08:54:56 -07:00
Windows Console Service Bot
6095d3c0cc Localization Updates - main - 07/18/2024 03:07:26 (#17584) 2024-07-22 08:54:06 -07:00
Leonard Hecker
0a2b660e64 AtlasEngine: Fix dirty rects during scrolling (#17583)
This regressed in #15707. By having the `viewportOffset` on the
`Settings` object we accidentally invalidate the entire viewport
every time it scrolls. That doesn't break anything of course,
but it's better to prevent this.

This PR additionally contains a fix for clamping the y coordinates
coming from `Renderer`: Since `viewportCellCount.y` is a count and
thus exclusive, but clamp's max is inclusive, we must subtract 1.
2024-07-22 14:48:52 +02:00
529 changed files with 15986 additions and 8547 deletions

View File

@@ -27,6 +27,7 @@ gje
godbolt
hyperlinking
hyperlinks
Kbds
kje
libfuzzer
liga
@@ -43,6 +44,7 @@ mkmk
mnt
mru
nje
NTMTo
notwrapped
ogonek
overlined

View File

@@ -96,6 +96,7 @@ IGraphics
IImage
IInheritable
IMap
imm
IMonarch
IObject
iosfwd
@@ -174,6 +175,7 @@ PALLOC
PATINVERT
PEXPLICIT
PICKFOLDERS
PINPUT
pmr
ptstr
QUERYENDSESSION

View File

@@ -128,3 +128,4 @@
^XamlStyler\.json$
ignore$
Resources/(?!en)
^\.vsconfig$

View File

@@ -9,7 +9,6 @@ ABORTIFHUNG
ACCESSTOKEN
acidev
ACIOSS
ACover
acp
actctx
ACTCTXW
@@ -38,6 +37,7 @@ ANSISYS
ANSISYSRC
ANSISYSSC
answerback
ANSWERBACKMESSAGE
antialiasing
ANull
anycpu
@@ -86,6 +86,7 @@ Autowrap
AVerify
awch
azurecr
AZZ
backgrounded
Backgrounder
backgrounding
@@ -142,8 +143,8 @@ BTNFACE
bufferout
buffersize
buflen
buildtransitive
buildsystems
buildtransitive
BValue
bytebuffer
cac
@@ -179,7 +180,6 @@ CFuzz
cgscrn
chafa
changelists
charinfo
CHARSETINFO
chh
chshdng
@@ -263,7 +263,6 @@ consolegit
consolehost
CONSOLEIME
consoleinternal
Consoleroot
CONSOLESETFOREGROUND
consoletaeftemplates
consoleuwp
@@ -385,7 +384,7 @@ DECCIR
DECCKM
DECCKSR
DECCOLM
DECCRA
deccra
DECCTR
DECDC
DECDHL
@@ -397,7 +396,7 @@ DECEKBD
DECERA
DECFI
DECFNK
DECFRA
decfra
DECGCI
DECGCR
DECGNL
@@ -726,7 +725,6 @@ GHIJKL
gitcheckin
gitfilters
gitlab
gitmodules
gle
GLOBALFOCUS
GLYPHENTRY
@@ -866,6 +864,7 @@ INLINEPREFIX
inproc
Inputkeyinfo
Inputreadhandledata
INPUTSCOPE
INSERTMODE
INTERACTIVITYBASE
INTERCEPTCOPYPASTE
@@ -915,6 +914,7 @@ Keymapping
keyscan
keystate
keyups
Kickstart
KILLACTIVE
KILLFOCUS
kinda
@@ -1020,7 +1020,6 @@ lstatus
lstrcmp
lstrcmpi
LTEXT
LTLTLTLTL
ltsc
LUID
luma
@@ -1115,7 +1114,6 @@ msrc
MSVCRTD
MTSM
Munged
munges
murmurhash
muxes
myapplet
@@ -1217,7 +1215,6 @@ ntlpcapi
ntm
ntrtl
ntstatus
NTSYSCALLAPI
nttree
nturtl
ntuser
@@ -1233,6 +1230,7 @@ NUMSCROLL
NUnit
nupkg
NVIDIA
NVT
OACR
objbase
ocolor
@@ -1293,6 +1291,7 @@ parms
PATCOPY
pathcch
PATTERNID
pbstr
pcb
pcch
PCCHAR
@@ -1378,9 +1377,11 @@ POSTCHARBREAKS
POSX
POSXSCROLL
POSYSCROLL
ppbstr
PPEB
ppf
ppidl
pprg
PPROC
ppropvar
ppsi
@@ -1407,6 +1408,7 @@ processenv
processhost
PROCESSINFOCLASS
PRODEXT
Productize
PROPERTYID
PROPERTYKEY
propertyval
@@ -1523,7 +1525,6 @@ rftp
rgbi
RGBQUAD
rgbs
rgci
rgfae
rgfte
rgn
@@ -1601,6 +1602,7 @@ SELECTALL
SELECTEDFONT
SELECTSTRING
Selfhosters
Serbo
SERVERDLL
SETACTIVE
SETBUDDYINT
@@ -1695,6 +1697,7 @@ srcsrv
SRCSRVTRG
srctool
srect
SRGS
srvinit
srvpipe
ssa
@@ -1813,6 +1816,7 @@ TITLEISLINKNAME
TJson
TLambda
TLDP
tldr
TLEN
TMAE
TMPF
@@ -1828,8 +1832,6 @@ TOPDOWNDIB
TOpt
tosign
touchpad
Tpp
Tpqrst
tracelogging
traceviewpp
trackbar
@@ -1842,7 +1844,6 @@ triaging
TRIMZEROHEADINGS
trx
tsa
tsattrs
tsgr
tsm
TStr
@@ -1954,7 +1955,6 @@ VPACKMANIFESTDIRECTORY
VPR
VREDRAW
vsc
vsconfig
vscprintf
VSCROLL
vsdevshell
@@ -1971,10 +1971,8 @@ vswhere
vtapp
VTE
VTID
vtio
vtmode
vtpipeterm
vtpt
VTRGB
VTRGBTo
vtseq
@@ -1996,7 +1994,6 @@ wcswidth
wddm
wddmcon
WDDMCONSOLECONTEXT
WDK
wdm
webpage
websites
@@ -2070,7 +2067,6 @@ winuserp
WINVER
wistd
wmain
wmemory
WMSZ
wnd
WNDALLOC
@@ -2137,6 +2133,7 @@ XBUTTONDOWN
XBUTTONUP
XCast
XCENTER
xchar
xcopy
XCount
xdy
@@ -2168,6 +2165,7 @@ yact
YCast
YCENTER
YCount
yizz
YLimit
YPan
YSubstantial
@@ -2181,3 +2179,4 @@ ZCtrl
ZWJs
ZYXWVU
ZYXWVUTd
zzf

View File

@@ -22,6 +22,7 @@ vcvars\w*
ROY\sG\.\sBIV
!(?:(?i)ESC)!\[
!(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
(?i)rgb:[a-z0-9]{2,4}/[a-z0-9]{2,4}/[a-z0-9]{2,4}
# SSE intrinsics like "_mm_subs_epu16"
\b_mm(?:|256|512)_\w+\b

View File

@@ -1,33 +0,0 @@
name: GitGudSimilarIssues comments
on:
issues:
types: [opened]
jobs:
getSimilarIssues:
runs-on: ubuntu-latest
outputs:
message: ${{ steps.getBody.outputs.message }}
steps:
- id: getBody
uses: craigloewen-msft/GitGudSimilarIssues@main
with:
issueTitle: ${{ github.event.issue.title }}
issueBody: ${{ github.event.issue.body }}
repo: ${{ github.repository }}
similaritytolerance: "0.8"
add-comment:
needs: getSimilarIssues
runs-on: ubuntu-latest
permissions:
issues: write
if: needs.getSimilarIssues.outputs.message != ''
steps:
- name: Add comment
run: gh issue comment "$NUMBER" --repo "$REPO" --body "$BODY"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUMBER: ${{ github.event.issue.number }}
REPO: ${{ github.repository }}
BODY: ${{ needs.getSimilarIssues.outputs.message }}

View File

@@ -1,35 +1,51 @@
{
"version": "1.0",
"components": [
"Microsoft.VisualStudio.Component.Roslyn.Compiler",
"Microsoft.Component.MSBuild",
"Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
"Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime",
"Microsoft.VisualStudio.Component.SQL.CLR",
"Microsoft.VisualStudio.Component.CoreEditor",
"Microsoft.VisualStudio.Workload.CoreEditor",
"Microsoft.VisualStudio.Workload.Universal",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
"Microsoft.Net.Component.4.8.SDK",
"Microsoft.Net.Component.4.7.2.TargetingPack",
"Microsoft.VisualStudio.Component.TextTemplating",
"Microsoft.VisualStudio.Component.NuGet",
"Microsoft.VisualStudio.Component.Roslyn.Compiler",
"Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
"Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
"Microsoft.Component.MSBuild",
"Microsoft.VisualStudio.Component.ManagedDesktop.Core",
"Microsoft.Net.Component.4.TargetingPack",
"Microsoft.Net.Component.4.5.TargetingPack",
"Microsoft.NetCore.Component.Runtime.8.0",
"Microsoft.NetCore.Component.SDK",
"Microsoft.VisualStudio.Component.AppInsights.Tools",
"Microsoft.Net.Component.4.8.TargetingPack",
"Microsoft.VisualStudio.Component.DiagnosticTools",
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
"Microsoft.NetCore.Component.Runtime.6.0",
"Microsoft.VisualStudio.Component.ClassDesigner",
"Microsoft.VisualStudio.Component.GraphDocument",
"Microsoft.VisualStudio.Component.CodeMap",
"Microsoft.VisualStudio.Component.VC.CoreIde",
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
"Microsoft.VisualStudio.Component.Graphics",
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
"Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging",
"Microsoft.VisualStudio.ComponentGroup.WindowsAppSDK.Cs",
"Microsoft.ComponentGroup.Blend",
"Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native",
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
"Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake",
"Microsoft.VisualStudio.Component.Vcpkg",
"Microsoft.Component.NetFX.Native",
"Microsoft.VisualStudio.Component.Graphics",
"Microsoft.VisualStudio.ComponentGroup.UWP.Xamarin",
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
"Microsoft.VisualStudio.Component.VC.Tools.ARM64EC",
"Microsoft.VisualStudio.Component.UWP.VC.ARM64EC",
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
"Microsoft.VisualStudio.Component.VC.ASAN",
"Microsoft.VisualStudio.Component.VC.v143.x86.x64",
"Microsoft.VisualStudio.Component.VC.v143.ARM64",
"Microsoft.VisualStudio.Component.UWP.VC.ARM64",
"Microsoft.VisualStudio.Component.VC.Tools.ARM",
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v143",
"Microsoft.VisualStudio.Component.UWP.VC.ARM64"
]
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.ComponentGroup.WindowsAppDevelopment.Prerequisites",
"Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard",
"Microsoft.VisualStudio.Workload.Universal"
],
"extensions": []
}

34
.wt.json Normal file
View File

@@ -0,0 +1,34 @@
{
"$version": "1.0.0",
"snippets":
[
{
"input": "bx\r",
"name": "Build project",
"description": "Build the project in the CWD"
},
{
"input": "bz\r",
"name": "Build solution, incremental",
"description": "Just build changes to the solution"
},
{
"input": "bcz\r",
"name": "Clean & build solution",
"icon": "\uE8e6",
"description": "Start over. Go get your coffee. "
},
{
"input": "nuget push -ApiKey az -source TerminalDependencies %userprofile%\\Downloads",
"name": "Upload package to nuget feed",
"icon": "\uE898",
"description": "Go download a .nupkg, put it in ~/Downloads, and use this to push to our private feed."
},
{
"input": "runut /name:**\u001b[D",
"name": "Run a test",
"icon": "",
"description": "Enter the name of a test to run"
}
]
}

View File

@@ -41,6 +41,11 @@
-->
<MSBuildCacheIdenticalDuplicateOutputPatterns>$(MSBuildCacheIdenticalDuplicateOutputPatterns);bin\**</MSBuildCacheIdenticalDuplicateOutputPatterns>
<!--
vcpkg is invoked for each and every vcxproj and each invocation uses the same base dir for logging, so allow these duplicate "outputs".
-->
<MSBuildCacheIdenticalDuplicateOutputPatterns>$(MSBuildCacheIdenticalDuplicateOutputPatterns);obj\*\vcpkg\**</MSBuildCacheIdenticalDuplicateOutputPatterns>
<!-- version of MSBuildCache is not part of the cache key -->
<PackagesConfigFile>$(MSBuildThisFileDirectory)\dep\nuget\packages.config</PackagesConfigFile>
<MSBuildCacheIgnoredInputPatterns>$(MSBuildCacheIgnoredInputPatterns);$(PackagesConfigFile)</MSBuildCacheIgnoredInputPatterns>

View File

@@ -48,6 +48,42 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
## chromium/base/numerics
**Source**: [https://github.com/chromium/chromium/tree/master/base/numerics](https://github.com/chromium/chromium/tree/master/base/numerics)
### License
```
Copyright 2015 The Chromium Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```
## \{fmt\}
**Source**: [https://github.com/fmtlib/fmt](https://github.com/fmtlib/fmt)

View File

@@ -1,5 +1,7 @@
![terminal-logos](https://github.com/microsoft/terminal/assets/91625426/333ddc76-8ab2-4eb4-a8c0-4d7b953b1179)
[![Terminal Build Status](https://dev.azure.com/shine-oss/terminal/_apis/build/status%2FTerminal%20CI?branchName=main)](https://dev.azure.com/shine-oss/terminal/_build/latest?definitionId=1&branchName=main)
# Welcome to the Windows Terminal, Console and Command-Line repo
This repository contains the source code for:
@@ -145,15 +147,6 @@ _Learn more about the [types of Windows Terminal distributions](https://learn.mi
The plan for the Windows Terminal [is described here](/doc/roadmap-2023.md) and
will be updated as the project proceeds.
## Project Build Status
Project|Build Status
---|---
Terminal|[![Terminal Build Status](https://dev.azure.com/ms/terminal/_apis/build/status/terminal%20CI?branchName=main)](https://dev.azure.com/ms/terminal/_build?definitionId=136)
ColorTool|![Colortool Build Status](https://microsoft.visualstudio.com/_apis/public/build/definitions/c93e867a-8815-43c1-92c4-e7dd5404f1e1/17023/badge)
---
## Terminal & Console Overview
Please take a few minutes to review the overview below before diving into the

View File

@@ -56,7 +56,15 @@ Dies ist ein Open Source-Projekt, und wir freuen uns über die Teilnahme der Com
<ReleaseNotes>
Version __VERSION_NUMBER__
Weitere Einzelheiten finden Sie auf der Seite der GitHub-Veröffentlichungen.
Wir haben umgeschrieben, wie Konsolenanwendungen im Terminal gehostet werden! Melden Sie alle auftretenden Fehler.
- Terminal unterstützt jetzt Sixels!
- Sie können jetzt ein angedocktes Fenster öffnen, das Ausschnitte von Befehlen enthält, die Sie gespeichert haben, um sie später zu verwenden.
- Für Benutzer der Eingabeaufforderung der neuesten Version von Windows 11 wird möglicherweise ein „Kurzer Tipp“-Symbol angezeigt, das installierbare Software von WinGet
vorschlägt
- Ausgewählter Text wird jetzt viel sichtbarer (und anpassbarer!)
- Eine Reihe von Zuverlässigkeitsfehlern, Komfortproblemen und Ärgernissen wurden behoben.
Weitere Informationen finden Sie auf unserer GitHub-Releaseseite.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,6 +56,13 @@ This is an open source project and we welcome community participation. To partic
<ReleaseNotes _locID="App_ReleaseNotes">
<!-- _locComment_text="{MaxLength=1500} {Locked=__VERSION_NUMBER__} App Release Note" -->Version __VERSION_NUMBER__
- We've rewritten how console applications are hosted inside Terminal! Please report any bugs you encounter.
- Terminal now supports Sixels!
- You can now open a docked panel containing snippets of commands you have saved to use later
- Command Prompt users on the latest Windows 11 release may see a "quick tip" icon that suggests installable software from WinGet
- Selected text will now be much more visible (and customizable!)
- A number of reliabilty bugs, convenience issues and annoyances have been fixed.
Please see our GitHub releases page for additional details.
</ReleaseNotes>
<ScreenshotCaptions>

View File

@@ -56,7 +56,14 @@ Este es un proyecto de fuente abierta y animamos a la comunidad a participar. Pa
<ReleaseNotes>
Versión __VERSION_NUMBER__
Para más información, consulte nuestra página de versiones de GitHub.
- Hemos reescrito cómo se hospedan las aplicaciones de consola en Terminal. Informe de los errores que encuentre.
- Terminal ahora admite sixeles.
- Ahora puede abrir un panel acoplado que contenga fragmentos de comandos que haya guardado para usarlos más adelante
- Los usuarios del símbolo del sistema de la versión más reciente de Windows 11 pueden ver un icono de "sugerencia rápida" que sugiere software instalable de WinGet
- El texto seleccionado ahora será mucho más visible (y personalizable)
- Se han corregido varios errores de fiabilidad, problemas de comodidad y molestias.
Consulte la página de versiones de GitHub para más información.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@ Il sagit dun projet open source et nous vous invitons à participer dans l
<ReleaseNotes>
Version __VERSION_NUMBER__
Consultez la page des versions de GitHub pour plus dinformations.
- Nous avons réécrit la manière dont les applications de console sont hébergées dans Terminal ! Veuillez signaler tout bug que vous rencontrez.
- Le terminal prend désormais en charge Sixels !
- Vous pouvez désormais ouvrir un panneau ancré contenant des extraits de commandes que vous avez enregistrées pour les utiliser ultérieurement
- Les utilisateurs de l'invite de commande sur la dernière version de Windows 11 peuvent voir une icône « astuce rapide » qui suggère un logiciel installable à partir de WinGet
- Le texte sélectionné sera désormais beaucoup plus visible (et personnalisable !)
- Un certain nombre de bugs de fiabilité, de problèmes de commodité et de désagréments ont été corrigés.
Veuillez consulter notre page de versions GitHub pour plus de détails.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -54,9 +54,16 @@ Si tratta di un progetto open source e la partecipazione della community è molt
</DevStudio>
<ReleaseNotes>
Versione __VERSION_NUMBER__
Versione __VERSION_NUMBER__
Per ulteriori dettagli, consulta la nostra pagina delle versioni di GitHub.
- È stato riscritto il modo in cui le applicazioni della console vengono ospitate all'interno di Terminale. Segnala eventuali bug riscontrati.
- Terminal supporta ora Sixel.
- È ora possibile aprire un pannello ancorato contenente frammenti di comandi salvati per usarli in seguito
- Gli utenti del prompt dei comandi nella versione più recente di Windows 11 potrebbero visualizzare un'icona di "suggerimento rapido" che consiglia il software installabile da WinGet
- Il testo selezionato sarà ora molto più visibile, oltre che personalizzabile.
- Sono stati risolti diversi bug di affidabilità, problemi di praticità e fastidi.
Per altri dettagli, vedi la pagina delle versioni di GitHub.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
バージョン __VERSION_NUMBER__
詳細については、GitHub リリースのページをご覧ください。
- ターミナル内でのコンソール アプリケーションのホスト方法を書き換えました。発生したバグを報告してください。
- ターミナルで Sixels がサポートされるようになりました。
- 後で使用するために保存したコマンドのスニペットを含むドッキング パネルを開けるようになりました
- 最新の Windows 11 リリースのコマンド プロンプト ユーザーには、WinGet からインストール可能なソフトウェアを提案する "クイック ヒント" アイコンが表示される場合があります
- 選択したテキストが大幅に見やすくなりました (カスタマイズも可能です)
- 信頼性に関するバグ、利便性の問題、不快な問題の多くが修正されました。
詳細については、GitHub リリース ページをご覧ください。
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,6 +56,13 @@
<ReleaseNotes>
버전 __VERSION_NUMBER__
- 콘솔 애플리케이션이 터미널 내에서 호스팅되는 방법을 다시 작성했습니다! 발생한 버그를 보고하세요.
- 터미널에서 이제 Sixels를 지원합니다!
- 이제 나중에 사용하기 위해 저장한 명령 조각이 포함된 도킹된 패널을 열 수 있습니다.
- 최신 Windows 11 릴리스의 명령 프롬프트 사용자는 WinGet에서 설치 가능한 소프트웨어를 제안하는 "빠른 팁" 아이콘을 볼 수 있습니다.
- 이제 선택한 텍스트가 훨씬 더 잘 표시됩니다(사용자 지정도 가능!).
- 여러 신뢰성 버그, 편의 문제 및 성가신 사항이 수정되었습니다.
자세한 내용은 GitHub 릴리스 페이지를 참조하세요.
</ReleaseNotes>
<ScreenshotCaptions>

View File

@@ -56,7 +56,14 @@ Este é um projeto de código aberto e a participação da comunidade é bem-vin
<ReleaseNotes>
Versão __VERSION_NUMBER__
Consulte nossa página de lançamentos do GitHub para obter detalhes adicionais.
- Reescrevemos a forma como os aplicativos de console são hospedados no Terminal! Certifique-se de reportar os bugs que você encontrar.
- O terminal agora é compatível com o Sixels!
- Agora você pode abrir um painel acoplado contendo snippets de comandos que você salvou para usar mais tarde
- Os usuários do Prompt de Comando na versão mais recente do Windows 11 podem ver um ícone de "dica rápida", que sugere softwares instaláveis a partir do WinGet
- O texto selecionado agora ficará muito mais visível (e personalizável!)
- Vários bugs de confiabilidade, problemas de conveniência e incômodos foram resolvidos.
Confira nossa página de lançamentos no GitHub para obter mais detalhes.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!
- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!!
- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! !
- Ä ņϋmъŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!!
Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!!
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!
- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!!
- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! !
- Ä ņϋmъŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!!
Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!!
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
- Ẁē'νё ŕéẁѓĭτťёñ ћοώ ĉòπşõℓε άррℓіċªťįõпѕ αяе ĥθѕťэđ įŋšιďé Ţєямїńąℓ! Рļéаšė яёροřτ αņу ьϋģš ýõμ éпćŏџήţęя. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!
- Ţëямΐʼnαļ ńóẃ ŝüррöятš Śїхέłś! !!! !!! !!!
- ¥оų ĉåи ńòŵ θρėñ д đбčĸэď ράńέļ ċőлŧăīņϊňģ śⁿіφφëťś оƒ ςōмmàⁿďş ŷŏũ ĥªν℮ şåνěđ τσ üśε łαťэŗ !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Ćοмmäлđ Рřōmφť üş℮ŗѕ öη τће ļāťëšτ Щīйđôώѕ 11 řёℓеаѕĕ måў ŝэε ά "qůïςκ ŧĭр" ιсôñ τĥдт šűğģєѕŧѕ ίńśŧăłłавļз šôƒţẁαгέ ƒґόm ЩĩйĞéţ !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Śєļèċťєď ţĕжт ωϊŀļ йǿẃ ьέ mџ¢н мǿѓε νĭŝϊъļė (άŋđ сŭŝтŏмΐżдьļē!) !!! !!! !!! !!! !!! !!! !
- Ä ņϋmъŗ ŏƒ ѓēŀїаъïļŧÿ ьüĝś, ςôⁿνėηĭ℮иć℮ îѕšůëş ăπð âлňбγдňçėŝ ћªνε ъēёп ƒΐ×еð. !!! !!! !!! !!! !!! !!! !!! !!!
Ρĺёàŝ℮ ŝез ǿúг ĢīťНŭъ řěłεαśèŝ φāğ℮ ƒóѓ дďδітĭøиąℓ ð℮тªїľŝ. !!! !!! !!! !!! !!! !!!
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Версия __VERSION_NUMBER__
Дополнительные сведения см. на странице «Выпуски GitHub».
Мы переписали, как консольные приложения размещаются внутри Терминала! Сообщайте о любых ошибках, с которыми вы столкнулись.
Терминал теперь поддерживает форматы Sixel!
Теперь вы можете открыть закрепленную панель, содержащую фрагменты команд, которые вы сохранили для использования в дальнейшем
Пользователи командной строки в новейшем выпуске Windows 11 могут увидеть значок "краткой подсказки", который предлагает устанавливаемые программы из WinGet
Выделенный текст теперь станет более видимым (и настраиваемым!)
Исправлено несколько ошибок надежности, проблем с удобством, а также устранены раздражающие моменты.
Дополнительные сведения см. на странице выпусков GitHub.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -54,7 +54,14 @@
</DevStudio>
<ReleaseNotes>
版本 __VERSION_NUMBER__
Version __VERSION_NUMBER__
- 我们已改变主机应用程序在终端内的托管方式!请报告遇到的任何 bug。
- 终端现在支持 Sixels!
- 现在可以打开一个停靠面板,其中包含已保存供以后使用的命令片段
- 最新 Windows 11 版本上的命令提示用户可能会看到“快速提示”图标,该图标建议从 WinGet 安装软件
- 所选文本现在将具有更高的可见性(和可自定义性!)
- 修复了许多可靠性 bug、便利性问题和令人烦恼的问题。
有关其他详细信息,请参阅我们的 GitHub 发布页面。
</ReleaseNotes>

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
版本 __VERSION_NUMBER__
如需詳細資訊,請參閱我們的 GitHub 版本頁面
- 我們已重寫主機應用程式在終端機內託管的方式!請報告您遇到的錯誤
- 終端機現在支援 Sixels!
- 現在,您可以開啟包含已儲存命令程式碼片段的固定面板,以供稍後使用
- 最新 Windows 11 版本中的 [命令提示] 使用者可能會看到「快速提示」圖示,建議可自 WinGet 安裝的軟體
- 選取的文字現在會更明顯 (且可自訂!)
- 已修正一些可靠性錯誤、便利性問題和令人困擾的問題。
如需更多詳細資訊,請參閱我們的 GitHub 發行頁面。
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@ Dies ist ein Open Source-Projekt, und wir freuen uns über die Teilnahme an der
<ReleaseNotes>
Version __VERSION_NUMBER__
Weitere Einzelheiten finden Sie auf der Seite der GitHub-Veröffentlichungen.
Terminal speichert jetzt den Inhalt des Fensters, wenn Sie die Sitzungswiederherstellung verwenden.
Sie können jetzt mehrere Schriftarten gleichzeitig verwenden.
Kästchenzeichnende Zeichen werden jetzt pixelgenau gerendert.
Die Verwendung eines IME innerhalb des Terminals wurde erheblich verbessert.
Die Farbschemas in Ihrer JSON-Datei sind jetzt viel einfacher.
Eine Reihe von Fehlern im Zusammenhang mit der URL-Verarbeitung, Zeilen mit doppelter Breite, Zeilenumbruch und mehr wurden behoben.
Weitere Informationen finden Sie auf unserer GitHub-Releaseseite.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,6 +56,13 @@ This is an open source project and we welcome community participation. To partic
<ReleaseNotes _locID="App_ReleaseNotes">
<!-- _locComment_text="{MaxLength=1500} {Locked=__VERSION_NUMBER__} App Release Note" -->Version __VERSION_NUMBER__
- Terminal will now remember the contents of the window when you use session restoration.
- You can now use multiple fonts at the same time.
- Box-drawing characters are now rendered with pixel perfection.
- The experience of using an IME inside Terminal has been significantly improved.
- The color schemes inside your JSON file will now be much simpler.
- A number of bugs around URL handling, double-width rows, line wrapping, and more have been fixed.
Please see our GitHub releases page for additional details.
</ReleaseNotes>
<ScreenshotCaptions>

View File

@@ -56,7 +56,14 @@ Este es un proyecto de fuente abierta y animamos a la comunidad a participar. Pa
<ReleaseNotes>
Versión __VERSION_NUMBER__
Para más información, consulte nuestra página de versiones de GitHub.
- Terminal recordará ahora el contenido de la ventana cuando use la restauración de la sesión.
- Ahora puede usar varias fuentes al mismo tiempo.
- Los caracteres que dibujan recuadros ahora se representan con precisión de píxel.
- Se ha mejorado significativamente la experiencia de utilizar un IME dentro de Terminal.
- Las combinaciones de colores dentro del archivo JSON ahora serán mucho más sencillas.
- Se han corregido varios errores relacionados con el control de direcciones URL, las filas de ancho doble, el ajuste de líneas y mucho más.
Consulte la página de versiones de GitHub para más información.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@ Il sagit dun projet open source et nous encourageons la participation à l
<ReleaseNotes>
Version __VERSION_NUMBER__
Consultez la page des versions de GitHub pour plus dinformations.
- Le terminal mémorisera désormais le contenu de la fenêtre lorsque vous utiliserez la restauration de session.
- Vous pouvez désormais utiliser plusieurs polices en même temps.
- Les personnages dessinés en boîte sont désormais rendus avec une perfection de pixel.
- L'expérience d'utilisation d'un IME dans le Terminal a été considérablement améliorée.
- Les schémas de couleurs à l'intérieur de votre fichier JSON seront désormais beaucoup plus simples.
- Un certain nombre de bugs concernant la gestion des URL, les lignes à double largeur, le retour à la ligne, etc. ont été corrigés.
Veuillez consulter notre page de versions GitHub pour plus de détails.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -54,9 +54,16 @@ Si tratta di un progetto open source e la partecipazione della community è molt
</DevStudio>
<ReleaseNotes>
Versione __VERSION_NUMBER__
Versione __VERSION_NUMBER__
Per ulteriori dettagli, consulta la nostra pagina delle versioni di GitHub.
- Il terminale ricorda ora il contenuto della finestra quando si usa il ripristino della sessione.
- È ora possibile usare più tipi di carattere contemporaneamente.
- I caratteri tracciati vengono ora sottoposti a rendering con pixel di perfezionamento.
- L'esperienza di utilizzo di un IME all'interno di Terminale è stata notevolmente migliorata.
- Le combinazioni di colori all'interno del file JSON saranno ora molto più semplici.
- Sono stati corretti alcuni bug relativi alla gestione degli URL, alle righe a doppia larghezza, al ritorno a capo delle righe e altro ancora.
Per altri dettagli, vedi la pagina delle versioni di GitHub.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
バージョン __VERSION_NUMBER__
詳細については、GitHub リリースのページをご覧ください
- セッションの復元を使用すると、ターミナルがウィンドウの内容を記憶するようになりました
- 複数のフォントを同時に使用できるようになりました。
- ボックス描画文字がピクセル単位の精度でレンダリングされるようになりました。
- ターミナル内での IME の使用エクスペリエンスが大幅に改善されました。
- JSON ファイル内の配色がはるかにシンプルになりました。
- URL 処理、二重幅の行、行の折り返しなどに関するいくつかのバグが修正されました。
詳細については、GitHub リリース ページをご覧ください。
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,6 +56,13 @@
<ReleaseNotes>
버전 __VERSION_NUMBER__
- 터미널은 이제 세션 복원을 사용할 때 창의 내용을 기억합니다.
- 이제 여러 글꼴을 동시에 사용할 수 있습니다.
- 상자 그리기 캐릭터가 이제 픽셀 완성도로 렌더링됩니다.
- 터미널 내에서 IME를 사용하는 환경이 크게 개선되었습니다.
- 이제 JSON 파일 내의 색 구성표가 훨씬 더 간단해집니다.
- URL 처리, 이중 너비 행, 줄 바꿈 등과 관련된 여러 버그가 수정되었습니다.
자세한 내용은 GitHub 릴리스 페이지를 참조하세요.
</ReleaseNotes>
<ScreenshotCaptions>

View File

@@ -56,7 +56,14 @@ Este é um projeto de código aberto e a participação da comunidade é bem-vin
<ReleaseNotes>
Versão __VERSION_NUMBER__
Consulte nossa página de lançamentos do GitHub para obter detalhes adicionais.
- O terminal agora se lembra do conteúdo da janela quando você usa a restauração de sessão.
- Agora você pode usar várias fontes ao mesmo tempo.
- Os caracteres da caixa de desenho agora são renderizados com a perfeição de pixels.
- A experiência de usar uma IME dentro do Terminal foi significativamente aprimorada.
- Os esquemas de cores dentro do seu arquivo JSON agora estão muito mais simples.
- Foram corrigidos vários bugs envolvendo o tratamento de URLs, linhas de largura dupla, quebra de linha automática e muito mais.
Confira nossa página de lançamentos no GitHub para obter mais detalhes.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
- Ŧēгмíйǻŀ шιļł ñσщ řėmėmвзґ τђз ςоńţëηťŝ σƒ ŧћé ẅιⁿδőщ ẅђеή ýóύ ŭš℮ şεššîóŋ řėşτŏѓдτіόŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Ύоџ ςàⁿ ŋóώ ũşэ múľŧìφľё ƒоʼnťş àт ťħе ѕâmз тìме. !!! !!! !!! !!! !!!
- Вό×-ðгăшĭиġ ¢ĥаяäςтеřѕ äřę ηоẁ ѓëńđêяεď ẁϊτђ φïжêĺ φėŗƒēςŧΐøй. !!! !!! !!! !!! !!! !!! !
- Ťħέ ĕхφêŕï℮ηĉε ŏƒ ύѕïйġ ǻʼn ÎМË îńšïďê Τєřmíлäļ нαŝ ьēέň ѕιĝήîƒіčäπţŀý ĩмφґθνзđ. !!! !!! !!! !!! !!! !!! !!! !!!
- Ťĥę čöℓοг şçђėmęš ιʼnśΐδê убџѓ ĴŠОИ ƒϊŀε ωĭŀł ʼnθω вз мúçĥ ѕїмρℓёґ. !!! !!! !!! !!! !!! !!! !!
- Á ήũmьéŕ òƒ вµġŝ άřòūñδ ÛҐĿ ħàŋδľįйģ, ðőџъŀε-ŵĭďτђ ŗōẁš, ŀϊπė ẃяąрρΐηğ, âⁿđ мŏř℮ ĥāνě везŋ ƒï×έð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
Ρļēªšê ŝέė őůг ĜīтĤųъ яëŀεäśēś рдġэ ƒõя ãδðìτϊöňãł δèτâĩĺѕ. !!! !!! !!! !!! !!! !!!
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
- Ŧēгмíйǻŀ шιļł ñσщ řėmėmвзґ τђз ςоńţëηťŝ σƒ ŧћé ẅιⁿδőщ ẅђеή ýóύ ŭš℮ şεššîóŋ řėşτŏѓдτіόŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Ύоџ ςàⁿ ŋóώ ũşэ múľŧìφľё ƒоʼnťş àт ťħе ѕâmз тìме. !!! !!! !!! !!! !!!
- Вό×-ðгăшĭиġ ¢ĥаяäςтеřѕ äřę ηоẁ ѓëńđêяεď ẁϊτђ φïжêĺ φėŗƒēςŧΐøй. !!! !!! !!! !!! !!! !!! !
- Ťħέ ĕхφêŕï℮ηĉε ŏƒ ύѕïйġ ǻʼn ÎМË îńšïďê Τєřmíлäļ нαŝ ьēέň ѕιĝήîƒіčäπţŀý ĩмφґθνзđ. !!! !!! !!! !!! !!! !!! !!! !!!
- Ťĥę čöℓοг şçђėmęš ιʼnśΐδê убџѓ ĴŠОИ ƒϊŀε ωĭŀł ʼnθω вз мúçĥ ѕїмρℓёґ. !!! !!! !!! !!! !!! !!! !!
- Á ήũmьéŕ òƒ вµġŝ άřòūñδ ÛҐĿ ħàŋδľįйģ, ðőџъŀε-ŵĭďτђ ŗōẁš, ŀϊπė ẃяąрρΐηğ, âⁿđ мŏř℮ ĥāνě везŋ ƒï×έð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
Ρļēªšê ŝέė őůг ĜīтĤųъ яëŀεäśēś рдġэ ƒõя ãδðìτϊöňãł δèτâĩĺѕ. !!! !!! !!! !!! !!! !!!
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Vėѓѕіöй __VERSION_NUMBER__ !!! !!! !
Рļєάśé ѕέę όüґ ĢίŧĦŭв řęļзąѕєš рαġè ƒőŗ äđδĭτíθņâℓ đέтαιľś. !!! !!! !!! !!! !!! !!!
- Ŧēгмíйǻŀ шιļł ñσщ řėmėmвзґ τђз ςоńţëηťŝ σƒ ŧћé ẅιⁿδőщ ẅђеή ýóύ ŭš℮ şεššîóŋ řėşτŏѓдτіόŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!!
- Ύоџ ςàⁿ ŋóώ ũşэ múľŧìφľё ƒоʼnťş àт ťħе ѕâmз тìме. !!! !!! !!! !!! !!!
- Вό×-ðгăшĭиġ ¢ĥаяäςтеřѕ äřę ηоẁ ѓëńđêяεď ẁϊτђ φïжêĺ φėŗƒēςŧΐøй. !!! !!! !!! !!! !!! !!! !
- Ťħέ ĕхφêŕï℮ηĉε ŏƒ ύѕïйġ ǻʼn ÎМË îńšïďê Τєřmíлäļ нαŝ ьēέň ѕιĝήîƒіčäπţŀý ĩмφґθνзđ. !!! !!! !!! !!! !!! !!! !!! !!!
- Ťĥę čöℓοг şçђėmęš ιʼnśΐδê убџѓ ĴŠОИ ƒϊŀε ωĭŀł ʼnθω вз мúçĥ ѕїмρℓёґ. !!! !!! !!! !!! !!! !!! !!
- Á ήũmьéŕ òƒ вµġŝ άřòūñδ ÛҐĿ ħàŋδľįйģ, ðőџъŀε-ŵĭďτђ ŗōẁš, ŀϊπė ẃяąрρΐηğ, âⁿđ мŏř℮ ĥāνě везŋ ƒï×έð. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
Ρļēªšê ŝέė őůг ĜīтĤųъ яëŀεäśēś рдġэ ƒõя ãδðìτϊöňãł δèτâĩĺѕ. !!! !!! !!! !!! !!! !!!
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
Версия __VERSION_NUMBER__
Дополнительные сведения см. на странице «Выпуски GitHub».
Терминал теперь будет запоминать содержимое окна при восстановлении сеанса.
Теперь вы можете использовать несколько шрифтов одновременно.
Символы псевдографики теперь отрисовываются с пиксельной точностью.
Значительно улучшена возможность использования IME внутри Терминала.
Цветовые схемы в JSON-файле теперь будут намного проще.
Исправлено несколько ошибок в обработке URL-адресов, строках двойной ширины, переносе строк и т. д.
Дополнительные сведения см. на странице выпусков GitHub.
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -54,7 +54,14 @@
</DevStudio>
<ReleaseNotes>
版本 __VERSION_NUMBER__
Version __VERSION_NUMBER__
- 现在,使用会话还原时,终端将记住窗口的内容。
- 现在可以同时使用多种字体。
- 现在以像素为单位呈现框绘图字符。
- 在终端内使用输入法的体验已得到显著提升。
- JSON 文件中的配色方案现在要简单得多。
- 已修复有关 URL 处理、双倍行宽、换行等大量 bug。
有关其他详细信息,请参阅我们的 GitHub 发布页面。
</ReleaseNotes>

View File

@@ -56,7 +56,14 @@
<ReleaseNotes>
版本 __VERSION_NUMBER__
如需詳細資訊,請參閱我們的 GitHub 版本頁面
- 當您使用工作階段還原時,終端機現在會記住視窗的內容
- 現在您可以同時使用多個字型。
- 製表格圖字元現在會以完美像素模式呈現。
- 在終端機內使用 IME 的體驗已大幅改善。
- JSON 檔案內的色彩配置現在將變得更簡單了。
- 已修正一些 URL 處理、雙寬度列、換行等相關錯誤。
如需更多詳細資訊,請參閱我們的 GitHub 發行頁面。
</ReleaseNotes>
<ScreenshotCaptions>
<!-- Valid length: 200 character limit, up to 9 elements per platform -->

View File

@@ -1,6 +1,6 @@
{
"instanceUrl": "https://microsoft.visualstudio.com",
"projectName": "OS",
"areaPath": "OS\\Windows Client and Services\\ADEPT\\E4D-Engineered for Developers\\SHINE\\Terminal",
"areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\Terminal",
"notificationAliases": ["condev@microsoft.com", "duhowett@microsoft.com"]
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
<package id="Microsoft.Taef" version="10.93.240607003" targetFramework="native" />
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
<package id="Microsoft.Debugging.Tools.PdbStr" version="20220617.1556.0" targetFramework="native" />
<package id="Microsoft.Debugging.Tools.SrcTool" version="20220617.1556.0" targetFramework="native" />

View File

@@ -28,7 +28,7 @@ extends:
official: true
branding: Canary
buildTerminal: true
pgoBuildMode: Optimize
pgoBuildMode: None # BODGY - OneBranch is on VS 17.10, which is known to be the worst
codeSign: true
signingIdentity:
serviceName: $(SigningServiceName)

View File

@@ -27,7 +27,7 @@ parameters:
- name: pgoBuildMode
displayName: "PGO Build Mode"
type: string
default: Optimize
default: None # BODGY - OneBranch is on VS 17.10, which is known to be the worst
values:
- Optimize
- Instrument

View File

@@ -59,10 +59,7 @@ jobs:
submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: steps-setup-versioning.yml
- template: steps-download-bin-dir-artifact.yml
parameters:

View File

@@ -159,10 +159,10 @@ jobs:
.\build\scripts\Generate-ThirdPartyNotices.ps1 -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
displayName: Generate NOTICE.html from NOTICE.md
- template: .\steps-restore-nuget.yml
- template: .\steps-install-vcpkg.yml
- template: .\steps-restore-nuget.yml
- pwsh: |-
.\build\scripts\Set-LatestVCToolsVersion.ps1
displayName: Work around DD-1541167 (VCToolsVersion)

View File

@@ -10,6 +10,6 @@ jobs:
submodules: false
clean: true
- powershell: |-
- pwsh: |-
.\build\scripts\Invoke-FormattingCheck.ps1
displayName: 'Run formatters'

View File

@@ -69,10 +69,9 @@ jobs:
fetchTags: false # Tags still result in depth > 1 fetch; we don't need them here
submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: steps-setup-versioning.yml
- template: steps-download-bin-dir-artifact.yml
parameters:
buildPlatforms: ${{ parameters.buildPlatforms }}

View File

@@ -57,10 +57,7 @@ jobs:
submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: steps-setup-versioning.yml
- template: steps-download-bin-dir-artifact.yml
parameters:

View File

@@ -44,10 +44,7 @@ jobs:
submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: steps-setup-versioning.yml
- task: DownloadPipelineArtifact@2
displayName: Download all PDBs from all prior build phases

View File

@@ -43,10 +43,7 @@ jobs:
submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: steps-setup-versioning.yml
- task: DownloadPipelineArtifact@2
displayName: Download all PDBs from all prior build phases

View File

@@ -38,10 +38,7 @@ jobs:
submodules: true
persistCredentials: True
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: steps-setup-versioning.yml
- task: DownloadPipelineArtifact@2
displayName: Download MSIX Bundle Artifact

View File

@@ -92,10 +92,7 @@ stages:
generateSbom: ${{ parameters.generateSbom }}
codeSign: ${{ parameters.codeSign }}
beforeBuildSteps: # Right before we build, lay down the universal package and localizations
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
- task: UniversalPackages@0
displayName: Download terminal-internal Universal Package
@@ -119,10 +116,7 @@ stages:
generateSbom: ${{ parameters.generateSbom }}
codeSign: ${{ parameters.codeSign }}
beforeBuildSteps:
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
# WPF doesn't need the localizations or the universal package, but if it does... put them here.
- stage: Package

View File

@@ -106,11 +106,6 @@ extends:
- stage: Build
displayName: Build
dependsOn: []
variables:
# This was set by the parent build, but we need to override it to select a specific compiler version
- template: ./build/pipelines/templates-v2/variables-onebranch-config.yml@self
parameters:
containerVersion: 1.0.02566.28
jobs:
- template: ./build/pipelines/templates-v2/job-build-project.yml@self
parameters:
@@ -135,10 +130,7 @@ extends:
codeSign: ${{ parameters.codeSign }}
signingIdentity: ${{ parameters.signingIdentity }}
beforeBuildSteps: # Right before we build, lay down the universal package and localizations
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
- task: UniversalPackages@0
displayName: Download terminal-internal Universal Package
@@ -172,10 +164,7 @@ extends:
codeSign: ${{ parameters.codeSign }}
signingIdentity: ${{ parameters.signingIdentity }}
beforeBuildSteps:
- task: PkgESSetupBuild@12
displayName: Package ES - Setup Build
inputs:
disableOutputRedirect: true
- template: ./build/pipelines/templates-v2/steps-setup-versioning.yml@self
# WPF doesn't need the localizations or the universal package, but if it does... put them here.
- stage: Package

View File

@@ -0,0 +1,6 @@
steps:
- pwsh: |-
nuget install Microsoft.Windows.Terminal.Versioning -OutputDirectory _versioning
$VersionRoot = (Get-Item _versioning\Microsoft.Windows.*).FullName
& "$VersionRoot\build\Setup.ps1" -ProjectDirectory "$(Build.SourcesDirectory)" -Verbose
displayName: Set up versioning via M.W.T.V

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
</PropertyGroup>
<ItemGroup>
<Reference Include="$(WinGetPackageRoot)\lib\Microsoft.Management.Deployment.winmd">
<IsWinMDFile>true</IsWinMDFile>
</Reference>
</ItemGroup>
<Target Name="_FixWinGetWinmdPackaging" BeforeTargets="_ComputeAppxPackagePayload">
<ItemGroup>
<PackagingOutputs Include="$(WinGetPackageRoot)\lib\Microsoft.Management.Deployment.winmd">
<OutputGroup>CustomOutputGroupForPackaging</OutputGroup>
<ProjectName>$(ProjectName)</ProjectName>
<TargetPath>Microsoft.Management.Deployment.winmd</TargetPath>
</PackagingOutputs>
</ItemGroup>
</Target>
</Project>

View File

@@ -5,7 +5,7 @@
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2024</XesBaseYearForStoreVersion>
<VersionMajor>1</VersionMajor>
<VersionMinor>22</VersionMinor>
<VersionMinor>23</VersionMinor>
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -104,6 +104,7 @@ GetConsoleKeyboardLayoutNameW(
#define CONSOLE_REGISTRY_DEFAULTFOREGROUND L"DefaultForeground"
#define CONSOLE_REGISTRY_DEFAULTBACKGROUND L"DefaultBackground"
#define CONSOLE_REGISTRY_TERMINALSCROLLING L"TerminalScrolling"
#define CONSOLE_REGISTRY_ANSWERBACKMESSAGE L"AnswerbackMessage"
// end V2 console settings
/*

View File

@@ -3,13 +3,14 @@
<packages>
<!-- Native packages -->
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
<package id="Microsoft.Taef" version="10.93.240607003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
<package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.7.230706001" targetFramework="native" />
<package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240122.1" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.WindowsPackageManager.ComInterop" version="1.8.1911" targetFramework="native" developmentDependency="true" />
<!-- Managed packages -->
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net45" />
@@ -19,7 +20,7 @@
<package id="Selenium.WebDriver" version="3.5.0" targetFramework="net45" />
<!-- MSBuildCache -->
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.273-preview" />
<package id="Microsoft.MSBuildCache.Local" version="0.1.273-preview" />
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.273-preview" />
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.283-preview" />
<package id="Microsoft.MSBuildCache.Local" version="0.1.283-preview" />
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.283-preview" />
</packages>

View File

@@ -618,6 +618,11 @@
"type": "boolean",
"default": false,
"description": "This will override the profile's `elevate` setting."
},
"reloadEnvironmentVariables": {
"type": "boolean",
"default": true,
"description": "When set to true, a new environment block will be generated when creating a new session. Otherwise, the session will inherit the variables the Terminal was started with."
}
},
"type": "object"
@@ -2335,7 +2340,7 @@
"description": "When set to `true`, the terminal window will auto-center itself on the display it opens on. The terminal will use the \"initialPosition\" to determine which display to open on.",
"type": "boolean"
},
"inputServiceWarning": {
"warning.inputService": {
"default": true,
"description": "Warning if 'Touch Keyboard and Handwriting Panel Service' is disabled.",
"type": "boolean"
@@ -2350,11 +2355,21 @@
"description": "When set to true, the terminal will focus the pane on mouse hover.",
"type": "boolean"
},
"compatibility.allowHeadless": {
"default": false,
"description": "When set to true, Windows Terminal will run in the background. This allows globalSummon and quakeMode actions to work even when no windows are open.",
"type": "boolean"
},
"compatibility.isolatedMode": {
"default": false,
"description": "When set to true, Terminal windows will not be able to interact with each other (including global hotkeys, tab drag/drop, running commandlines in existing windows, etc.). This is a compatibility escape hatch for users who are running into certain windowing issues.",
"type": "boolean"
},
"compatibility.allowDECRQCRA": {
"default": false,
"description": "When set to true, the terminal will support the DECRQCRA (Request Checksum of Rectangular Area) escape sequence.",
"type": "boolean"
},
"copyFormatting": {
"default": true,
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.",
@@ -2385,12 +2400,12 @@
"description": "When set to `true`, visual animations will be disabled across the application.",
"type": "boolean"
},
"largePasteWarning": {
"warning.largePaste": {
"default": true,
"description": "When set to true, trying to paste text with more than 5 KiB of characters will display a warning asking you whether to continue or not with the paste.",
"type": "boolean"
},
"multiLinePasteWarning": {
"warning.multiLinePaste": {
"default": true,
"description": "When set to true, trying to paste text with a \"new line\" character will display a warning asking you whether to continue or not with the paste.",
"type": "boolean"
@@ -2588,7 +2603,7 @@
"description": "Determines the delimiters used in a double click selection.",
"type": "string"
},
"confirmCloseAllTabs": {
"warning.confirmCloseAllTabs": {
"default": true,
"description": "When set to \"true\" closing a window with multiple tabs open will require confirmation. When set to \"false\", the confirmation dialog will not appear.",
"type": "boolean"
@@ -2850,7 +2865,7 @@
}
},
"autoMarkPrompts": {
"default": false,
"default": true,
"description": "When set to true, prompts will automatically be marked.",
"type": "boolean"
},
@@ -3032,6 +3047,10 @@
"description": "By default Windows treats Ctrl+Alt as an alias for AltGr. When altGrAliasing is set to false, this behavior will be disabled.",
"type": "boolean"
},
"answerbackMessage": {
"description": "The response that is sent when an ENQ control character is received.",
"type": "string"
},
"source": {
"description": "Stores the name of the profile generator that originated this profile.",
"type": [

View File

@@ -0,0 +1,840 @@
---
author: Leonard Hecker @lhecker
created on: 2024-06-07
last updated: 2024-06-07
issue id: 13000
---
# In-process ConPTY
## tl;dr
**Why?**
* Out-of-process leads to out-of-sync issues.
* Not all Console APIs have a VT equivalent.
* Time consuming maintenance work due to poor encapsulation inside conhost.
**How?**
1. Remove `VtEngine` and translate Console API calls directly to VT.
2. Move all Console API related code from the `Host` to the `Server` project.
Narrow the `IApiRoutines` interface down to its essentials.
Replace relevant singletons with instances.
3. Make `Server` a standalone library with `IApiRoutines` as its primary callback interface.
Integrate the library in Windows Terminal.
4. (Long term goal:) Ship the `Server` library as part of Windows. Build conhost itself on top of it.
## Why?
**Before:**
```mermaid
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
flowchart TD
subgraph conhost
direction LR
subgraph Server
server_io[Console API calls]
end
subgraph ConPTY
VtIo
VtEngine
ConPTY_output[ConPTY Output]
end
conhost_parser[VT parser]
conhost_buffer[Text Buffer]
conhost_renderer[Render Thread]
conhost_atlas[Text Renderer]
server_io-->conhost_parser
server_io--scroll, clear, etc.-->VtIo
conhost_parser--resize, focus-->VtIo
VtIo--scroll, clear, etc.-->VtEngine
conhost_parser--unknown escape sequences-->VtEngine
conhost_buffer--manual flushing-->VtEngine
conhost_renderer-->VtEngine
VtEngine-->ConPTY_output
conhost_buffer--dirty rects-->conhost_renderer
conhost_parser--dirty rects-->conhost_renderer
conhost_parser--plain text-->conhost_buffer
conhost_renderer-->conhost_atlas
conhost_buffer--ReadBuffer-->server_io
end
subgraph wt[Windows Terminal]
direction LR
input[ConPTY Input]
parser[VT parser]
buffer[Text Buffer]
renderer[Render Thread]
atlas[Text Renderer]
input-->parser
parser-->buffer
buffer-->renderer
renderer-->atlas
parser-->renderer
end
conhost--ConPTY's text pipe across processes-->wt
```
**After:**
```mermaid
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
flowchart LR
subgraph ConPTY_lib["ConPTY (static library)"]
server_io[Console API calls]
ConPTY_Translation[VT Translation]
server_io<-->ConPTY_Translation
end
subgraph ConPTY["ConPTY (dynamic library)"]
ConPTY_impl[IApiRoutines impl]
ConPTY_parser[VT parser]
ConPTY_buffer[Text Buffer]
ConPTY_output[ConPTY Output]
ConPTY_Translation<-->ConPTY_impl
ConPTY_impl-->ConPTY_parser
ConPTY_parser-->ConPTY_buffer
ConPTY_buffer--ReadBuffer-->ConPTY_impl
ConPTY_impl------>ConPTY_output
end
subgraph conhost
conhost_impl[IApiRoutines impl]
conhost_parser[VT parser]
conhost_buffer[Text Buffer]
conhost_renderer[Render Thread]
conhost_atlas[Text Renderer]
ConPTY_Translation<-->conhost_impl
conhost_impl-->conhost_parser
conhost_parser-->conhost_buffer
conhost_buffer--->conhost_renderer
conhost_buffer--ReadBuffer-->conhost_impl
conhost_renderer-->conhost_atlas
end
subgraph wt[Windows Terminal]
impl[IApiRoutines impl]
parser[VT parser]
buffer[Text Buffer]
renderer[Render Thread]
atlas[Text Renderer]
ConPTY_Translation<-->impl
impl-->parser
parser-->buffer
buffer--->renderer
buffer--ReadBuffer-->impl
renderer-->atlas
end
```
To extend on the [tl;dr](#tldr):
* ConPTY runs outside the hosting terminal which leads to an unsolvable issue: The buffer contents between ConPTY and the terminal can go out of sync.
* The terminal and ConPTY may implement escape sequences differently.
* ...may implement text processing differently.
* ...may implement text reflow (resize) differently.
* Resizing the terminal and ConPTY is asynchronous and there may be concurrent text output.
* ...and it may uncover text from the scrollback, which ConPTY doesn't know about.
* Some Console API methods cannot be represented via escape sequences and so ConPTY cannot produce them either.
The most basic example of this is the lack of LVB gridlines.
* The above suggested new architecture represents a significant simplification with no loss in features.
* ConPTY has fulfilled our needs a thousand times over, but as it's layered on top of conhost it has resulted in software decay.
The layer boundary has blurred over the years resulting in debugging and maintenance difficulties.
Lastly, its performance is insufficient and has been subject to much debate.
It's on us now to pay our debt and clean up the architecture so that ConPTY, conhost, and Windows Terminal can be built on top of it.
Considerations:
* A named `MUTEX` can theoretically solve parts of the out-of-sync issue, because it could be used to synchronize buffer reflow.
However, this requires the lock to be acquired on every API call, on top of the regular console lock, which raises ABBA deadlock concerns.
Making this setup not just hopefully but also provably robust, is likely to be very difficult.
It also doesn't solve any of the other listed problems.
## Goal 1: Remove VtEngine
### Goal
```mermaid
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
flowchart TD
subgraph conhost
direction LR
subgraph Server
server_io[Console API calls]
end
subgraph ConPTY
VtIo
VtEngine
ConPTY_output[ConPTY Output]
end
conhost_parser[VT parser]
conhost_buffer[Text Buffer]
conhost_renderer[Render Thread]
conhost_atlas[Text Renderer]
server_io-->conhost_parser
server_io--VT translation-->VtIo
server_io-->VtIo
conhost_parser-->VtIo
VtIo-->VtEngine
conhost_parser-->VtEngine
conhost_buffer-->VtEngine
conhost_renderer-->VtEngine
VtIo-->ConPTY_output
VtEngine-->ConPTY_output
conhost_buffer--dirty rects-->conhost_renderer
conhost_parser-->conhost_renderer
conhost_parser--plain text-->conhost_buffer
conhost_renderer-->conhost_atlas
conhost_buffer--ReadBuffer-->server_io
end
subgraph wt[Windows Terminal]
direction LR
input[ConPTY Input]
parser[VT parser]
buffer[Text Buffer]
renderer[Render Thread]
atlas[Text Renderer]
input-->parser
parser-->buffer
buffer-->renderer
renderer-->atlas
parser-->renderer
end
conhost--ConPTY's text pipe across processes-->wt
linkStyle 2,3,4,5,6,7,9,11 opacity:0.2
style VtEngine opacity:0.2
```
### Goals
* Remove VtEngine
* Remove `--vtmode`
* Remove `--resizeQuirk`
* Add buffering to `VtIo` and hook it up to the output pipe
* Implement direct VT translations for all relevant Console APIs
### Discussion
The idea is that we can translate Console API calls directly to VT at least as well as the current VtEngine setup can.
For instance, a call to `SetConsoleCursorPosition` clearly translates directly to a `CUP` escape sequence.
Effectively, instead of translating output asynchronously in the renderer thread, we'll do it synchronously right during the Console API call.
Apart from the Console APIs, the "cooked read" implementation, which handles our builtin "readline"-like text editor, will need to receive some larger changes as well.
Its popups use `ReadConsoleOutput` and `WriteConsoleOutput` to backup and restore the affected rectangle.
It also directly interfaces with the backing text buffer and its translation to VT relies on the existence of VtEngine.
This results in all of the same issues that were previously outlined.
In order to solve this, the popups need to be rewritten to use escape sequences so that they can be directly passed to the hosting terminal.
They should also always be below the current prompt line so that we don't need to perform a potentially lossy backup/restore operation.
The `--vtmode xterm-ascii` switch exists for the telnet client as it only supports ASCII as per RFC854 section "THE NVT PRINTER AND KEYBOARD".
However, telnet is the only user of this flag and it's trivial to do there (for instance by stripping high codepoints in `WriteOutputToClient` in `telnet/console.cpp`), so there's no reason for us to keep this logic in the new code.
This change will result in a significant reduction in complexity of our architecture.
VT input from the shell or other clients will be given 1:1 to the hosting terminal, which will resolve our ordering and buffering issues.
## Goal 2: Move Console API implementations to Server
### Goal
```mermaid
%%{ init: { "flowchart": { "curve": "monotoneX" } } }%%
flowchart TD
subgraph conhost
direction LR
subgraph Server[Server w/ ConPTY]
server_io[Console API calls]
ConPTY_Translation[VT Translation]
server_io<-->ConPTY_Translation
end
subgraph ConPTY
VtIo
ConPTY_output[ConPTY Output]
VtIo-->ConPTY_output
end
conhost_impl[IApiRoutines impl]
conhost_parser[VT parser]
conhost_buffer[Text Buffer]
conhost_renderer[Render Thread]
conhost_atlas[Text Renderer]
output_new[ConPTY Output]
ConPTY_Translation<-->conhost_impl
conhost_impl-->conhost_parser
conhost_impl----->output_new
server_io---->VtIo
server_io-->conhost_parser
conhost_parser-->conhost_buffer
conhost_parser-->conhost_renderer
conhost_buffer-->conhost_renderer
conhost_buffer--ReadBuffer-->conhost_impl
conhost_buffer-->server_io
conhost_renderer-->conhost_atlas
end
subgraph wt[Windows Terminal]
direction LR
input[ConPTY Input]
parser[VT parser]
buffer[Text Buffer]
renderer[Render Thread]
atlas[Text Renderer]
input-->parser
parser-->buffer
buffer-->renderer
renderer-->atlas
parser-->renderer
end
conhost--ConPTY's text pipe across processes-->wt
linkStyle 1,5,6,11 opacity:0.2
style ConPTY opacity:0.2
style VtIo opacity:0.2
style ConPTY_output opacity:0.2
```
### Goals
* Move the API implementation from the `Host` to the `Server` project.
* Narrow down the `IApiRoutines` interface to its essentials.
* Replace affected singletons with regular class instances.
### Discussion
The basic idea is that instead of having 3 arrows going in and out of the Server component, we got exactly 1.
This makes the console server and its VT translation a reusable component, which we need so that we can solve the out-of-sync issues by integrating it into Windows Terminal.
To make the Server API convenient to use, the interface needs to be narrowed down to as few methods as possible. This interface is currently called `IApiRoutines`.
### API Design
Design goals:
* Low overhead<br>
The API should never be the reason why the terminal is slow.
Among others, the design includes both UTF8 and UTF16 functions, as both encodings are common on Windows and the server component should not make assumptions which one the terminal prefers.
* Easy to use<br>
The Console API is unfortunately quite powerful.
In order to make implementing the callback interface still reasonably easy, the API should have as few methods as are needed.
If a complex Console API call can be expressed as a series of simpler ones, and if no other performance expectations exist, it should be expressed via the simpler ones.
* Works even if only partially implemented<br>
Example: `CreateConsoleScreenBuffer` is seldomly used, but its existence adds significant complexity to the callback API design and potential implementations.
A terminal should either be able to return `E_NOTIMPL` and we provide a fallback, or we provide guidance for how reasonable fallbacks can be implemented (e.g. by using the xterm alt buffer in this example).
> [!IMPORTANT]
> The following API design is a rough draft just to convey the general idea.
> It does not represent a complete, finished design.
> This document will be updated once work on the API begins and the actual API requirements become clearer.
```cs
// The Console API is built around a freely addressable frame buffer. It allows you to
// address any part of the buffer, even those outside of what's considered the "viewport":
//
// ┌──────────────────┐
// │y=-3 │
// │ │ ╮
// │ │ │
// ╭ ├──────────────────┤ ├── VT scrollback (partially addressed)
// │ │y=0 │ │
// │ │ │ ╮ ╯
// │ │ │ │
// Console Buffer ──┤ ├──────────────────┤ ├──── VT Viewport (top y = 1)
// │ │y=3 │ │
// │ │ │ ╯
// │ │ │
// ╰ └──────────────────┘
//
// The good news is that nothing prevents you from giving the Console Buffer the exact
// same size as the VT Viewport and for modern terminals doing so is recommended.
// That way, coordinates are viewport-relative and content below/above the viewport is never addressed:
//
// ┌──────────────────┐ ╮
// │y=-3 │ │
// │ │ ├── VT scrollback (unused)
// │ │ │
// ╭ ├──────────────────┤ ╮ ╯
// │ │y=0 │ │
// Console Buffer ──┤ │ │ ├──── VT Viewport
// │ │ │ │
// ╰ └──────────────────┘ ╯
//
// Coordinates are 0-indexed. Note that while INT32 coordinates are used by this API, coordinates below
// 0 and above 65535 are generally invalid as the Console ABI currently uses unsigned 16-Bit integers.
struct CONSRV_POINT_I32 {
INT32 x;
INT32 y;
};
struct CONSRV_POINT_F32 {
float x;
float y;
};
// These flags are also defined via Windows.h.
#if 0
// These flags are equivalent to the classic 4-bit indexed colors in VT via SGR.
// However, the position of the blue and red bits are swapped.
#define FOREGROUND_BLUE 0x0001 // Text color contains blue.
#define FOREGROUND_GREEN 0x0002 // Text color contains green.
#define FOREGROUND_RED 0x0004 // Text color contains red.
#define FOREGROUND_INTENSITY 0x0008 // Text color is intensified.
#define BACKGROUND_BLUE 0x0010 // Background color contains blue.
#define BACKGROUND_GREEN 0x0020 // Background color contains green.
#define BACKGROUND_RED 0x0040 // Background color contains red.
#define BACKGROUND_INTENSITY 0x0080 // Background color is intensified.
// These two bits are used to represent wide glyphs.
#define COMMON_LVB_LEADING_BYTE 0x0100 // Leading byte.
#define COMMON_LVB_TRAILING_BYTE 0x0200 // Trailing byte.
// This bit is equivalent to the "CSI 7 m" reverse video escape sequence.
#define COMMON_LVB_REVERSE_VIDEO 0x4000 // Reverse foreground and background attribute.
// NOTE: These flags have no equivalent in VT. COMMON_LVB_UNDERSCORE in particular is not the same as a
// "CSI 4 m" underline in VT, despite the name. They're used to give a cell border (= grid) lines.
#define COMMON_LVB_GRID_HORIZONTAL 0x0400 // Top horizontal.
#define COMMON_LVB_GRID_LVERTICAL 0x0800 // Left vertical.
#define COMMON_LVB_GRID_RVERTICAL 0x1000 // Right vertical.
#define COMMON_LVB_UNDERSCORE 0x8000 // Underscore.
#endif
// This struct is binary compatible to the CHAR_INFO struct from the Windows API and functionally equivalent.
//
// The following rules MUST be followed:
// * Each instance represents 1 column in the terminal.
// * Any cells that aren't representable with a single UCS-2 character, or are wider than 2 columns,
// must be replaced with U+FFFD. Grapheme clusters, surrogate pairs, and similar, are not allowed.
// Keep in mind that U+FFFD is a narrow character. It does not get the wide wide-glyph treatment below.
// * Colors that cannot be represented via the `attributes` flags can be replaced with an approximation.
// Alternatively, `FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED` can be used (no `BACKGROUND` bit set).
// * If a wide glyph (2 columns wide) is encountered, the following applies:
// * Create 2 consecutive CONSRV_CHAR_INFO instances (as per the first rule).
// * Repeat the same `character` in both instances, even if it's U+FFFD.
// * Assign the first instance an `attributes` flag of `COMMON_LVB_LEADING_BYTE`
// and the second one `COMMON_LVB_TRAILING_BYTE`.
// * BUT if the request for a CONSRV_CHAR_INFO only partially intersects a wide glyph, replace the character
// (and only the character) with U+0020 whitespace. This also means that `COMMON_LVB_LEADING/TRAILING_BYTE`
// should not be set, because the returned character isn't wide anymore. An example:
// If you have a red "猫" on top of a blue background in the buffer and a single `CONSRV_CHAR_INFO` is requested
// for the left half of the glyph, then you should set `character` to whitespace and `attributes` to
// `FOREGROUND_RED | BACKGROUND_BLUE`. Debug builds of ConPTY will assert that you do this.
//
// For more documentation about the `attributes` flags, see the constants defined above.
struct CONSRV_CHAR_INFO {
wchar_t character;
UINT16 attributes;
};
struct CONSRV_UTF8_STRING {
[size_is(length)] const char* data;
DWORD length;
};
struct CONSRV_UTF16_STRING {
[size_is(length)] const wchar_t* data;
DWORD length;
};
// NOTE: At the time of writing the required fields are not fully known.
// NOTE: boolean is 8 Bits large.
struct CONSRV_INFO {
// NOTE: msys2 relies on the HWND value to uniquely identify terminal sessions.
// If we were to hand out the multiplexed terminal window HWND to msys2, it will break. Either we need to
// create fake windows inside conhost (very bad & buggy) or break msys2 intentionally (also very bad).
HWND window;
CONSRV_UTF16_STRING originalWindowTitle;
CONSRV_UTF16_STRING windowTitle;
CONSRV_POINT_I32 bufferSizeInCells;
CONSRV_POINT_I32 cursorPositionInCells;
CONSRV_POINT_I32 viewPositionInCells;
CONSRV_POINT_I32 viewSizeInCells;
CONSRV_POINT_F32 cellSizeInDIP;
COLORREF colorTable[16];
CONSRV_POINT_I32 selectionStart;
CONSRV_POINT_I32 selectionEnd;
boolean selectionActive;
boolean selectionRectangular;
boolean selectionMouseDown;
float cursorHeight;
boolean cursorHidden;
};
// Any item that has changed relative to the current CONSRV_INFO will be set to a non-null pointer.
// In other words, members that are null represent those that remain unchanged.
//
// If the request cannot be supported return E_INVALIDARG. For instance, you may choose to
// do so if you receive a `bufferSizeInCells` change while the xterm alt buffer is active.
//
// NOTE: At the time of writing the required fields are not fully known.
struct CONSRV_INFO_CHANGE {
CONSRV_POINT_I32* bufferSizeInCells;
CONSRV_POINT_I32* cursorPositionInCells;
CONSRV_POINT_I32* viewPositionInCells;
CONSRV_POINT_I32* viewSizeInCells;
COLORREF* colorTable; // The referenced array is always 16 items large.
float* cursorHeight;
boolean* cursorHidden;
CONSRV_UTF16_STRING* fontName;
UINT32* fontFamily;
UINT32* fontWeight;
CONSRV_POINT_I32* fontSize;
};
interface IConsoleServer : IUnknown {
// TODO: This interface is incomplete. Among others, a way to launch new application into the server is missing.
// ConPTY manages stdin as a ring buffer for you. When the terminal has focus, you simply need to write your input.
// Keyboard input MUST be written via `WriteInputRecords`. The other 2 functions DO NOT parse any VT sequences.
// They're instead meant either for VT responses (DECRPM, etc.) and for dumping plain text (clipboard, etc.).
void WriteInputRecords([in] DWORD count, [in, length_is(count)] const INPUT_RECORD* records);
void WriteInputUTF8([in] CONSRV_UTF8_STRING text);
void WriteInputUTF16([in] CONSRV_UTF16_STRING text);
};
// First of all: You don't need to implement all functions and all structs perfectly for ConPTY to work decently well.
// For instance, if you don't implement `CONSRV_INFO::cursorHeight` properly, barely anything will happen.
interface IConsoleServerCallback : IUnknown {
// The console server is single-threaded and no two calls will be made simultaneously. These two functions
// simply allow you to synchronize the calls down below if your application is multi-threaded.
//
// Lock() will always be called before any of the functions below are called.
// Lock() and Unlock() do not need to support recursive locking.
// Any other calls between Lock() and Unlock() should be treated as an atomic operation.
//
// It is recommended to use a fair lock instead of OS primitives like SRWLOCK. These callback functions may be
// much more often than your text renderer, etc., runs. An unfair lock will result in thread starvation.
HRESULT Lock();
HRESULT Unlock();
// If called, you're requested to create a new console alt buffer. The Console API supports having
// multiple concurrent such buffers. They're not the same as the xterm alt buffer, however (CSI ? 1049 h):
// They can be resized to be larger than the current viewport and switching between such buffers DOES NOT
// clear them nor does it reset any other per-buffer state.
//
// If you have trouble adding support for multiple console alt buffers, consider using the xterm alt buffer
// (CSI ? 1049 h) for the first buffer that gets created, and return E_OUTOFMEMORY for any further buffers.
HRESULT CreateBuffer([out] void** buffer);
// ReleaseBuffer is called once the buffer isn't needed anymore. It's guaranteed to be called and it's guaranteed
// to be called after ActivateBuffer() was used to switch to another buffer (or the main buffer).
HRESULT ReleaseBuffer([in] void* buffer);
// This switches between different console alt buffers. Switching to a buffer should change the content that's
// being shown, similar to the xterm alt buffer, however unlike it doing so DOES NOT reset any per-buffer state.
// All it does is to basically swap out the underlying, active text buffer of the terminal.
//
// If `buffer` is NULL it's a request to switch back to the main buffer.
//
// `temporary` is a hint. If it's `true` it indicates that the previous buffer will soon be activated again.
// In other words, on Unlock() the buffer was active during Lock() will be active again.
// It's recommended that temporary switches are lightweight as they may occur relatively often.
HRESULT ActivateBuffer([in] void* buffer, [in] boolean temporary);
//
// Any functions past this point operate on the currently active buffer.
//
// This function gets a snapshot of the terminal and buffer state.
//
// You must ensure that the returned pointer stays valid until the next GetInfo() or Unlock() call,
// or until the currently active buffer is released.
// You don't need to return a new instance on each call. ConPTY will only use the last returned pointer.
// You don't need to keep the CONSRV_INFO struct constantly up to date, but you're allowed to do so.
// For instance, it's valid to change the `.cursorPosition` when SetCursorPosition() is called.
//
// It's recommended that this function is lightweight as it may be called relatively often.
const CONSRV_INFO* GetInfo();
// When this method is called you're asked to apply any non-null member of the given CONSRV_INFO_CHANGE struct
// to the active buffer. For instance a non-null `.cursorPosition` is identical to calling `SetCursorPosition`,
// a non-null `.bufferSize` is a request to resize the terminal, and so on.
HRESULT SetInfo([in] const CONSRV_INFO_CHANGE* info);
// As explained in the CONSRV_POINT_I32 documentation, ConPTY coordinates may be outside of the VT viewport.
// This function is necessary in order to support this. If you assign the console buffer the same size
// as the VT viewport, `pos` can be translated to VT using
// printf("\x1b[%d;%dC", pos.y + 1, pos.x + 1);
HRESULT SetCursorPosition([in] CONSRV_POINT_I32 pos);
// The Console API supports 4 gridline attributes which cannot be translated to VT.
// This function is necessary to represent those. If you don't plan to support the gridlines,
// you can translate the attributes to VT with the following code or some equivalent:
// static const uint8_t lut[] = { 30, 34, 32, 36, 31, 35, 33, 37, 90, 94, 92, 96, 91, 95, 93, 97 };
// const auto fg = lut[attributes & 0xf];
// const auto bg = lut[(attributes >> 4) & 0xf] + 10;
// printf("\x1b[%d;%dm", fg, bg);
//
// `attributes` of exactly `FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED` are often used to indicate the
// default colors in Windows Console applications, and so you may choose to translate attributes like that as:
// printf("\x1b[39;49");
//
// You may also choose to support COMMON_LVB_REVERSE_VIDEO, which translates to:
// printf("\x1b[7m");
HRESULT SetCurrentAttributes([in] UINT16 attributes);
// Starting from column `pos.x` in row `pos.y`, this reads `count`-many characters and attributes.
// `pos` and `count` will be clamped such that reads never extend outside of the `CONSRV_INFO::bufferSize`.
//
// However, it may still read cells that have never been written to (for instance below the current viewport!).
// Such reads should not fail. Simply fill the `infos` array with whitespaces and a default attribute of your chosing,
// but `FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED` is recommended (no other bits set).
//
// NOTE that this API should ignore any line renditions (DECDWL, DECDHL), margins (DECSLRM, ...), etc.
// Reading outside of the "valid" range for a given row should behave exactly like reading below the viewport,
// as described in the previous paragraph.
HRESULT ReadBuffer([in] CONSRV_POINT_I32 pos, [in] INT32 count, [out, length_is(count)] CONSRV_CHAR_INFO* infos);
// These two functions are used to layout text for the internal "GNU Readline"-like implementation.
// `text` is the string to operate on. As with any other method, input validation should be performed.
// It's preferred to pretend as if invalid codepoints (in particular invalid surrogate pairs)
// are U+FFFD, because this provides the user with some level of text editing capability.
// The alternative is to have none at all when facing invalid strings which is strictly worse.
// `maxClusters` is the maximum amount of "cursor movements" these functions should apply
// (like when pressing the left/right arrow buttons).
// `maxColumns` is the maximum amount of columns the functions may iterate over. When the text is "a猫" and
// `maxColumns` is 2, then the result should be "a", because "猫" doesn't fit anymore.
// `position` on input contains the current position of the cursor inside `text`, counted in characters from the
// start of the `text`. On output it's supposed to contain the new cursor position.
// `position` may be out of bounds and you should clamp it to a valid range first.
// `columns` on output should contain the number of columns that have been iterated over.
//
// The idea is that a `maxClusters = 1` and `maxColumns = inf` can be used to implement left/right cursor movement,
// while `maxClusters = inf` and `maxColumns = window width` can be used to layout text within the window.
//
// You don't need to handle escape characters. These functions will never be called with any present.
// For robustness against bugs it's however recommended to handle them anyway, in whatever way you wish.
// If you have no preference, it's recommended to treat them as zero-width characters.
HRESULT MeasureTextForward([in] CONSRV_UTF16_STRING text, [in] DWORD maxClusters, [in] DWORD maxColumns, [in, out] DWORD* position, [out] DWORD* columns);
HRESULT MeasureTextBackward([in] CONSRV_UTF16_STRING text, [in] DWORD maxClusters, [in] DWORD maxColumns, [in, out] DWORD* position, [out] DWORD* columns);
// UTF8 and UTF16 are both widely used text encodings on Windows and it's recommended that both
// functions are reasonably fast. ConPTY will translate all non-Unicode text to UTF16 for you.
// You must validate incoming text. It's recommended to replace invalid codepoints with U+FFFD.
// You don't need to check for broken up codepoints at the start/end of the text, as ConPTY will handle that for you.
HRESULT WriteUTF8([in] boolean raw, [in] CONSRV_UTF8_STRING text);
HRESULT WriteUTF16([in] boolean raw, [in] CONSRV_UTF16_STRING text);
};
```
The list shows how each Console API function is implemented in terms of the above interface.
* Aliases
<br>Fully implemented inside the server component without API.
* `AddConsoleAlias`
* `GetConsoleAlias`
* `GetConsoleAliases`
* `GetConsoleAliasesLength`
* `GetConsoleAliasExes`
* `GetConsoleAliasExesLength`
* History
<br>Fully implemented inside the server component without API.
* `ExpungeConsoleCommandHistory`
* `GetConsoleCommandHistory`
* `GetConsoleCommandHistoryLength`
* `GetConsoleHistoryInfo`
* `SetConsoleHistoryInfo`
* `SetConsoleNumberOfCommands`
* stdin
<br>Fully implemented inside the server component without API.
* `FlushConsoleInputBuffer`
* `GetConsoleInput`
* `GetConsoleInputCodePage`
* `GetConsoleInputMode`
* `GetNumberOfConsoleInputEvents`
* `ReadConsole`
* `SetConsoleInputCodePage`
* `SetConsoleInputMode`
* `WriteConsoleInput`
* Unsupported since conhost v1
* `GetConsoleDisplayMode`
* `GetConsoleLangId`
* `SetConsoleDisplayMode`
* Buffer management
* `GetConsoleScreenBufferInfoEx`:
Gets information from server's internal `SCREEN_INFORMATION` class (which represents the `HANDLE`).
* `SetConsoleScreenBufferInfoEx`:
Sets information on server's internal `SCREEN_INFORMATION` class.
* `CreateConsoleScreenBuffer`:
`CreateBuffer`
* `SetConsoleActiveScreenBuffer`:
`ActivateBuffer` + `SetInfo`
* `SetConsoleScreenBufferSize`:
`SetInfo`
* Cursor
* `GetConsoleCursorInfo`:
`GetInfo`
* `SetConsoleCursorInfo`:
`SetInfo`
* `SetConsoleCursorPosition`:
`GetInfo`
* Fonts
* `GetConsoleFontSize`:
`GetInfo`
* `GetCurrentConsoleFontEx`:
`GetInfo`
* `SetCurrentConsoleFontEx`:
`SetInfo`
* Window management
* `GetConsoleOriginalTitle`:
`GetInfo`
* `GetConsoleSelectionInfo`:
`GetInfo`
* `GetConsoleTitle`:
`GetInfo`
* `GetConsoleWindow`:
`GetInfo`
* `GetLargestConsoleWindowSize`:
`GetInfo`; The window frame size can be inferred from the difference between the `GetWindowRect(hwnd)` and the `viewSizeInCells * cellSizeInDIP`.
The max. cell count can then be calculated by getting the `MonitorFromWindow(hwnd)` size, subtracting the frame size and calculating the cell count.
* `GetNumberOfConsoleMouseButtons`:
Implemented inside the server component via `GetSystemMetrics(SM_CMOUSEBUTTONS)`
* `SetConsoleTitle`:
`SetInfo`
* `SetConsoleWindowInfo`:
`SetInfo`
* stdout (writing)
* `FillConsoleOutputAttribute`:
Set the new attributes with `SetCurrentAttributes`.
For each line, get the existing contents with `ReadBuffer`, `SetCursorPosition` to the start, concatenate the cells and write them with `WriteUTF16`.
* `FillConsoleOutputCharacter`:
For each line, get the existing contents with `ReadBuffer`, `SetCursorPosition` to the start, concatenate the cells and write them with `WriteUTF16`.
At the start of each line and every time the attributes change use `SetCurrentAttributes` to set them up.
* `GetConsoleOutputCodePage`:
Implemented inside the server component.
* `GetConsoleOutputMode`:
Implemented inside the server component.
* `ScrollConsoleScreenBuffer`:
**TODO**: It may be necessary to add a `ScrollBuffer` API to make vertical scrolling across the entire buffer width faster. This fast pass currently exists as well.
Otherwise, this will be translated to: Read all lines in the source rectangle with `ReadBuffer`.
Then refer to the `WriteConsoleOutput` implementation for writing it to the target.
* `SetConsoleOutputCodePage`:
Implemented inside the server component.
* `SetConsoleOutputMode`:
Implemented inside the server component.
* `SetConsoleTextAttribute`:
`SetCurrentAttributes`
* `WriteConsole`:
`WriteUTF8` if `CP_UTF8` is active and otherwise `WriteUTF16`.
* `WriteConsoleOutput`:
For each line, `SetCursorPosition` to the start, concatenate the cells and write them with `WriteUTF16`.
At the start of each line and every time the attributes change use `SetCurrentAttributes` to set them up.
* `WriteConsoleOutputAttribute`:
Same as `FillConsoleOutputAttribute`, but with varying attributes.
* `WriteConsoleOutputCharacter`:
Same as `FillConsoleOutputCharacter`, but with varying characters.
* stdout (reading)
<br>Each of these will be translated to a series of `ReadBuffer` calls, one for each line.
* `ReadConsoleOutput`
* `ReadConsoleOutputAttribute`
* `ReadConsoleOutputCharacter`
## Goal 3: Productize Server
### Goal
```mermaid
flowchart LR
subgraph ConPTY_lib["ConPTY (static library)"]
server_io[Console API calls]
ConPTY_Translation[VT Translation]
server_io<-->ConPTY_Translation
end
subgraph ConPTY["ConPTY (dynamic library)"]
ConPTY_impl[IApiRoutines impl]
ConPTY_parser[VT parser]
ConPTY_buffer[Text Buffer]
ConPTY_output[ConPTY Output]
ConPTY_Translation<-->ConPTY_impl
ConPTY_impl-->ConPTY_parser
ConPTY_parser-->ConPTY_buffer
ConPTY_buffer--ReadBuffer-->ConPTY_impl
ConPTY_impl------>ConPTY_output
end
subgraph conhost
subgraph conhost_Server[Server]
conhost_server_io[Console API calls]
conhost_ConPTY_Translation[VT Translation]
conhost_server_io<-->conhost_ConPTY_Translation
end
conhost_impl[IApiRoutines impl]
conhost_parser[VT parser]
conhost_buffer[Text Buffer]
conhost_renderer[Render Thread]
conhost_atlas[Text Renderer]
output_new[ConPTY Output]
conhost_ConPTY_Translation<-->conhost_impl
conhost_impl-.remains for<br>back compat.....->output_new
ConPTY_Translation<-->conhost_impl
conhost_impl-->conhost_parser
conhost_parser-->conhost_buffer
conhost_buffer--->conhost_renderer
conhost_buffer--ReadBuffer-->conhost_impl
conhost_renderer-->conhost_atlas
end
subgraph wt[Windows Terminal]
impl[IApiRoutines impl]
parser[VT parser]
buffer[Text Buffer]
renderer[Render Thread]
atlas[Text Renderer]
ConPTY_Translation<-->impl
impl-->parser
parser-->buffer
buffer--->renderer
buffer--ReadBuffer-->impl
renderer-->atlas
end
linkStyle 6,7 opacity:0.2
style conhost_Server opacity:0.2
style conhost_server_io opacity:0.2
style conhost_ConPTY_Translation opacity:0.2
```
After creating an _internal_ API in conhost, the next logical step is for us to extract the interface as a public one and consume it in Windows Terminal.
At this point we should also see if we can find early adopters of the API in other projects.
## Stretch Goal 4: Ship Server in Windows
The console server on Windows has been an internal ABI for the longest time.
As we have started shipping ConPTY bundled with conhost/OpenConsole to other projects, the internal ABI has become a public one.
While we have no intention of ever breaking forward compatability of the console server with future Windows versions, it would be reassuring if the ABI long-term became internal once more.
This will allow us complete freedom over its design, even in the decades to come, and allow us to fully shim any 3rd party applications on Windows if a ABI change were to ever happen.

27
oss/chromium/LICENSE Normal file
View File

@@ -0,0 +1,27 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,17 @@
### Notes for Future Maintainers
This was originally imported by @miniksa in January 2020.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
## What should be done to update this in the future?
1. Go to chromium/chromium repository on GitHub.
2. Take the entire contents of the base/numerics directory wholesale and drop it in the base/numerics directory here.
3. Don't change anything about it.
4. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in the same directory as this readme.
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
5. Submit the pull.

View File

@@ -0,0 +1,28 @@
# Copyright (c) 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This is a dependency-free, header-only, library, and it needs to stay that
# way to facilitate pulling it into various third-party projects. So, this
# file is here to protect against accidentally introducing external
# dependencies or depending on internal implementation details.
source_set("base_numerics") {
visibility = [ "//base/*" ]
sources = [
"checked_math_impl.h",
"clamped_math_impl.h",
"safe_conversions_arm_impl.h",
"safe_conversions_impl.h",
"safe_math_arm_impl.h",
"safe_math_clang_gcc_impl.h",
"safe_math_shared_impl.h",
]
public = [
"checked_math.h",
"clamped_math.h",
"math_constants.h",
"ranges.h",
"safe_conversions.h",
"safe_math.h",
]
}

View File

@@ -0,0 +1,7 @@
# This is a dependency-free, header-only, library, and it needs to stay that
# way to facilitate pulling it into various third-party projects. So, this
# file is here to protect against accidentally introducing dependencies.
include_rules = [
"-base",
"+base/numerics",
]

View File

@@ -0,0 +1,5 @@
jschuh@chromium.org
tsepez@chromium.org
# COMPONENT: Internals

View File

@@ -0,0 +1,409 @@
# `base/numerics`
This directory contains a dependency-free, header-only library of templates
providing well-defined semantics for safely and performantly handling a variety
of numeric operations, including most common arithmetic operations and
conversions.
The public API is broken out into the following header files:
* `checked_math.h` contains the `CheckedNumeric` template class and helper
functions for performing arithmetic and conversion operations that detect
errors and boundary conditions (e.g. overflow, truncation, etc.).
* `clamped_math.h` contains the `ClampedNumeric` template class and
helper functions for performing fast, clamped (i.e. [non-sticky](#notsticky)
saturating) arithmetic operations and conversions.
* `safe_conversions.h` contains the `StrictNumeric` template class and
a collection of custom casting templates and helper functions for safely
converting between a range of numeric types.
* `safe_math.h` includes all of the previously mentioned headers.
*** aside
**Note:** The `Numeric` template types implicitly convert from C numeric types
and `Numeric` templates that are convertable to an underlying C numeric type.
The conversion priority for `Numeric` type coercions is:
* `StrictNumeric` coerces to `ClampedNumeric` and `CheckedNumeric`
* `ClampedNumeric` coerces to `CheckedNumeric`
***
[TOC]
## Common patterns and use-cases
The following covers the preferred style for the most common uses of this
library. Please don't cargo-cult from anywhere else. 😉
### Performing checked arithmetic type conversions
The `checked_cast` template converts between arbitrary arithmetic types, and is
used for cases where a conversion failure should result in program termination:
```cpp
// Crash if signed_value is out of range for buff_size.
size_t buff_size = checked_cast<size_t>(signed_value);
```
### Performing saturated (clamped) arithmetic type conversions
The `saturated_cast` template converts between arbitrary arithmetic types, and
is used in cases where an out-of-bounds source value should be saturated to the
corresponding maximum or minimum of the destination type:
```cpp
// Convert from float with saturation to INT_MAX, INT_MIN, or 0 for NaN.
int int_value = saturated_cast<int>(floating_point_value);
```
### Enforcing arithmetic type conversions at compile-time
The `strict_cast` emits code that is identical to `static_cast`. However,
provides static checks that will cause a compilation failure if the
destination type cannot represent the full range of the source type:
```cpp
// Throw a compiler error if byte_value is changed to an out-of-range-type.
int int_value = strict_cast<int>(byte_value);
```
You can also enforce these compile-time restrictions on function parameters by
using the `StrictNumeric` template:
```cpp
// Throw a compiler error if the size argument cannot be represented by a
// size_t (e.g. passing an int will fail to compile).
bool AllocateBuffer(void** buffer, StrictCast<size_t> size);
```
### Comparing values between arbitrary arithmetic types
Both the `StrictNumeric` and `ClampedNumeric` types provide well defined
comparisons between arbitrary arithmetic types. This allows you to perform
comparisons that are not legal or would trigger compiler warnings or errors
under the normal arithmetic promotion rules:
```cpp
bool foo(unsigned value, int upper_bound) {
// Converting to StrictNumeric allows this comparison to work correctly.
if (MakeStrictNum(value) >= upper_bound)
return false;
```
*** note
**Warning:** Do not perform manual conversions using the comparison operators.
Instead, use the cast templates described in the previous sections, or the
constexpr template functions `IsValueInRangeForNumericType` and
`IsTypeInRangeForNumericType`, as these templates properly handle the full range
of corner cases and employ various optimizations.
***
### Calculating a buffer size (checked arithmetic)
When making exact calculations—such as for buffer lengths—it's often necessary
to know when those calculations trigger an overflow, undefined behavior, or
other boundary conditions. The `CheckedNumeric` template does this by storing
a bit determining whether or not some arithmetic operation has occured that
would put the variable in an "invalid" state. Attempting to extract the value
from a variable in an invalid state will trigger a check/trap condition, that
by default will result in process termination.
Here's an example of a buffer calculation using a `CheckedNumeric` type (note:
the AssignIfValid method will trigger a compile error if the result is ignored).
```cpp
// Calculate the buffer size and detect if an overflow occurs.
size_t size;
if (!CheckAdd(kHeaderSize, CheckMul(count, kItemSize)).AssignIfValid(&size)) {
// Handle an overflow error...
}
```
### Calculating clamped coordinates (non-sticky saturating arithmetic)
Certain classes of calculations—such as coordinate calculations—require
well-defined semantics that always produce a valid result on boundary
conditions. The `ClampedNumeric` template addresses this by providing
performant, non-sticky saturating arithmetic operations.
Here's an example of using a `ClampedNumeric` to calculate an operation
insetting a rectangle.
```cpp
// Use clamped arithmetic since inset calculations might overflow.
void Rect::Inset(int left, int top, int right, int bottom) {
origin_ += Vector2d(left, top);
set_width(ClampSub(width(), ClampAdd(left, right)));
set_height(ClampSub(height(), ClampAdd(top, bottom)));
}
```
*** note
<a name="notsticky"></a>
The `ClampedNumeric` type is not "sticky", which means the saturation is not
retained across individual operations. As such, one arithmetic operation may
result in a saturated value, while the next operation may then "desaturate"
the value. Here's an example:
```cpp
ClampedNumeric<int> value = INT_MAX;
++value; // value is still INT_MAX, due to saturation.
--value; // value is now (INT_MAX - 1), because saturation is not sticky.
```
***
## Conversion functions and StrictNumeric<> in safe_conversions.h
This header includes a collection of helper `constexpr` templates for safely
performing a range of conversions, assignments, and tests.
### Safe casting templates
* `as_signed()` - Returns the supplied integral value as a signed type of
the same width.
* `as_unsigned()` - Returns the supplied integral value as an unsigned type
of the same width.
* `checked_cast<>()` - Analogous to `static_cast<>` for numeric types, except
that by default it will trigger a crash on an out-of-bounds conversion (e.g.
overflow, underflow, NaN to integral) or a compile error if the conversion
error can be detected at compile time. The crash handler can be overridden
to perform a behavior other than crashing.
* `saturated_cast<>()` - Analogous to `static_cast` for numeric types, except
that it returns a saturated result when the specified numeric conversion
would otherwise overflow or underflow. An NaN source returns 0 by
default, but can be overridden to return a different result.
* `strict_cast<>()` - Analogous to `static_cast` for numeric types, except
this causes a compile failure if the destination type is not large
enough to contain any value in the source type. It performs no runtime
checking and thus introduces no runtime overhead.
### Other helper and conversion functions
* `IsValueInRangeForNumericType<>()` - A convenience function that returns
true if the type supplied as the template parameter can represent the value
passed as an argument to the function.
* `IsTypeInRangeForNumericType<>()` - A convenience function that evaluates
entirely at compile-time and returns true if the destination type (first
template parameter) can represent the full range of the source type
(second template parameter).
* `IsValueNegative()` - A convenience function that will accept any
arithmetic type as an argument and will return whether the value is less
than zero. Unsigned types always return false.
* `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer
parameter as an unsigned result (thus avoiding an overflow if the value
is the signed, two's complement minimum).
### StrictNumeric<>
`StrictNumeric<>` is a wrapper type that performs assignments and copies via
the `strict_cast` template, and can perform valid arithmetic comparisons
across any range of arithmetic types. `StrictNumeric` is the return type for
values extracted from a `CheckedNumeric` class instance. The raw numeric value
is extracted via `static_cast` to the underlying type or any type with
sufficient range to represent the underlying type.
* `MakeStrictNum()` - Creates a new `StrictNumeric` from the underlying type
of the supplied arithmetic or StrictNumeric type.
* `SizeT` - Alias for `StrictNumeric<size_t>`.
## CheckedNumeric<> in checked_math.h
`CheckedNumeric<>` implements all the logic and operators for detecting integer
boundary conditions such as overflow, underflow, and invalid conversions.
The `CheckedNumeric` type implicitly converts from floating point and integer
data types, and contains overloads for basic arithmetic operations (i.e.: `+`,
`-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers).
However, *the [variadic template functions
](#CheckedNumeric_in-checked_math_h-Non_member-helper-functions)
are the prefered API,* as they remove type ambiguities and help prevent a number
of common errors. The variadic functions can also be more performant, as they
eliminate redundant expressions that are unavoidable with the with the operator
overloads. (Ideally the compiler should optimize those away, but better to avoid
them in the first place.)
Type promotions are a slightly modified version of the [standard C/C++ numeric
promotions
](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
with the two differences being that *there is no default promotion to int*
and *bitwise logical operations always return an unsigned of the wider type.*
### Members
The unary negation, increment, and decrement operators are supported, along
with the following unary arithmetic methods, which return a new
`CheckedNumeric` as a result of the operation:
* `Abs()` - Absolute value.
* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
(valid for only integral types).
* `Max()` - Returns whichever is greater of the current instance or argument.
The underlying return type is whichever has the greatest magnitude.
* `Min()` - Returns whichever is lowest of the current instance or argument.
The underlying return type is whichever has can represent the lowest
number in the smallest width (e.g. int8_t over unsigned, int over
int8_t, and float over int).
The following are for converting `CheckedNumeric` instances:
* `type` - The underlying numeric type.
* `AssignIfValid()` - Assigns the underlying value to the supplied
destination pointer if the value is currently valid and within the
range supported by the destination type. Returns true on success.
* `Cast<>()` - Instance method returning a `CheckedNumeric` derived from
casting the current instance to a `CheckedNumeric` of the supplied
destination type.
*** aside
The following member functions return a `StrictNumeric`, which is valid for
comparison and assignment operations, but will trigger a compile failure on
attempts to assign to a type of insufficient range. The underlying value can
be extracted by an explicit `static_cast` to the underlying type or any type
with sufficient range to represent the underlying type.
***
* `IsValid()` - Returns true if the underlying numeric value is valid (i.e.
has not wrapped or saturated and is not the result of an invalid
conversion).
* `ValueOrDie()` - Returns the underlying value. If the state is not valid
this call will trigger a crash by default (but may be overridden by
supplying an alternate handler to the template).
* `ValueOrDefault()` - Returns the current value, or the supplied default if
the state is not valid (but will not crash).
**Comparison operators are explicitly not provided** for `CheckedNumeric`
types because they could result in a crash if the type is not in a valid state.
Patterns like the following should be used instead:
```cpp
// Either input or padding (or both) may be arbitrary sizes.
size_t buff_size;
if (!CheckAdd(input, padding, kHeaderLength).AssignIfValid(&buff_size) ||
buff_size >= kMaxBuffer) {
// Handle an error...
} else {
// Do stuff on success...
}
```
### Non-member helper functions
The following variadic convenience functions, which accept standard arithmetic
or `CheckedNumeric` types, perform arithmetic operations, and return a
`CheckedNumeric` result. The supported functions are:
* `CheckAdd()` - Addition.
* `CheckSub()` - Subtraction.
* `CheckMul()` - Multiplication.
* `CheckDiv()` - Division.
* `CheckMod()` - Modulus (integer only).
* `CheckLsh()` - Left integer shift (integer only).
* `CheckRsh()` - Right integer shift (integer only).
* `CheckAnd()` - Bitwise AND (integer only with unsigned result).
* `CheckOr()` - Bitwise OR (integer only with unsigned result).
* `CheckXor()` - Bitwise XOR (integer only with unsigned result).
* `CheckMax()` - Maximum of supplied arguments.
* `CheckMin()` - Minimum of supplied arguments.
The following wrapper functions can be used to avoid the template
disambiguator syntax when converting a destination type.
* `IsValidForType<>()` in place of: `a.template IsValid<>()`
* `ValueOrDieForType<>()` in place of: `a.template ValueOrDie<>()`
* `ValueOrDefaultForType<>()` in place of: `a.template ValueOrDefault<>()`
The following general utility methods is are useful for converting from
arithmetic types to `CheckedNumeric` types:
* `MakeCheckedNum()` - Creates a new `CheckedNumeric` from the underlying type
of the supplied arithmetic or directly convertible type.
## ClampedNumeric<> in clamped_math.h
`ClampedNumeric<>` implements all the logic and operators for clamped
(non-sticky saturating) arithmetic operations and conversions. The
`ClampedNumeric` type implicitly converts back and forth between floating point
and integer data types, saturating on assignment as appropriate. It contains
overloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for
all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison
operators for arithmetic types of any size. However, *the [variadic template
functions
](#ClampedNumeric_in-clamped_math_h-Non_member-helper-functions)
are the prefered API,* as they remove type ambiguities and help prevent
a number of common errors. The variadic functions can also be more performant,
as they eliminate redundant expressions that are unavoidable with the operator
overloads. (Ideally the compiler should optimize those away, but better to avoid
them in the first place.)
Type promotions are a slightly modified version of the [standard C/C++ numeric
promotions
](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions)
with the two differences being that *there is no default promotion to int*
and *bitwise logical operations always return an unsigned of the wider type.*
*** aside
Most arithmetic operations saturate normally, to the numeric limit in the
direction of the sign. The potentially unusual cases are:
* **Division:** Division by zero returns the saturated limit in the direction
of sign of the dividend (first argument). The one exception is 0/0, which
returns zero (although logically is NaN).
* **Modulus:** Division by zero returns the dividend (first argument).
* **Left shift:** Non-zero values saturate in the direction of the signed
limit (max/min), even for shifts larger than the bit width. 0 shifted any
amount results in 0.
* **Right shift:** Negative values saturate to -1. Positive or 0 saturates
to 0. (Effectively just an unbounded arithmetic-right-shift.)
* **Bitwise operations:** No saturation; bit pattern is identical to
non-saturated bitwise operations.
***
### Members
The unary negation, increment, and decrement operators are supported, along
with the following unary arithmetic methods, which return a new
`ClampedNumeric` as a result of the operation:
* `Abs()` - Absolute value.
* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type
(valid for only integral types).
* `Max()` - Returns whichever is greater of the current instance or argument.
The underlying return type is whichever has the greatest magnitude.
* `Min()` - Returns whichever is lowest of the current instance or argument.
The underlying return type is whichever has can represent the lowest
number in the smallest width (e.g. int8_t over unsigned, int over
int8_t, and float over int).
The following are for converting `ClampedNumeric` instances:
* `type` - The underlying numeric type.
* `RawValue()` - Returns the raw value as the underlying arithmetic type. This
is useful when e.g. assigning to an auto type or passing as a deduced
template parameter.
* `Cast<>()` - Instance method returning a `ClampedNumeric` derived from
casting the current instance to a `ClampedNumeric` of the supplied
destination type.
### Non-member helper functions
The following variadic convenience functions, which accept standard arithmetic
or `ClampedNumeric` types, perform arithmetic operations, and return a
`ClampedNumeric` result. The supported functions are:
* `ClampAdd()` - Addition.
* `ClampSub()` - Subtraction.
* `ClampMul()` - Multiplication.
* `ClampDiv()` - Division.
* `ClampMod()` - Modulus (integer only).
* `ClampLsh()` - Left integer shift (integer only).
* `ClampRsh()` - Right integer shift (integer only).
* `ClampAnd()` - Bitwise AND (integer only with unsigned result).
* `ClampOr()` - Bitwise OR (integer only with unsigned result).
* `ClampXor()` - Bitwise XOR (integer only with unsigned result).
* `ClampMax()` - Maximum of supplied arguments.
* `ClampMin()` - Minimum of supplied arguments.
The following is a general utility method that is useful for converting
to a `ClampedNumeric` type:
* `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type
of the supplied arithmetic or directly convertible type.

View File

@@ -0,0 +1,393 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_CHECKED_MATH_H_
#define BASE_NUMERICS_CHECKED_MATH_H_
#include <stddef.h>
#include <limits>
#include <type_traits>
#include "base/numerics/checked_math_impl.h"
namespace base {
namespace internal {
template <typename T>
class CheckedNumeric {
static_assert(std::is_arithmetic<T>::value,
"CheckedNumeric<T>: T must be a numeric type.");
public:
using type = T;
constexpr CheckedNumeric() = default;
// Copy constructor.
template <typename Src>
constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
: state_(rhs.state_.value(), rhs.IsValid()) {}
template <typename Src>
friend class CheckedNumeric;
// This is not an explicit constructor because we implicitly upgrade regular
// numerics to CheckedNumerics to make them easier to use.
template <typename Src>
constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
: state_(value) {
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
}
// This is not an explicit constructor because we want a seamless conversion
// from StrictNumeric types.
template <typename Src>
constexpr CheckedNumeric(
StrictNumeric<Src> value) // NOLINT(runtime/explicit)
: state_(static_cast<Src>(value)) {}
// IsValid() - The public API to test if a CheckedNumeric is currently valid.
// A range checked destination type can be supplied using the Dst template
// parameter.
template <typename Dst = T>
constexpr bool IsValid() const {
return state_.is_valid() &&
IsValueInRangeForNumericType<Dst>(state_.value());
}
// AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
// and is within the range supported by the destination type. Returns true if
// successful and false otherwise.
template <typename Dst>
#if defined(__clang__) || defined(__GNUC__)
__attribute__((warn_unused_result))
#elif defined(_MSC_VER)
_Check_return_
#endif
constexpr bool
AssignIfValid(Dst* result) const {
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
? ((*result = static_cast<Dst>(state_.value())), true)
: false;
}
// ValueOrDie() - The primary accessor for the underlying value. If the
// current state is not valid it will CHECK and crash.
// A range checked destination type can be supplied using the Dst template
// parameter, which will trigger a CHECK if the value is not in bounds for
// the destination.
// The CHECK behavior can be overridden by supplying a handler as a
// template parameter, for test code, etc. However, the handler cannot access
// the underlying value, and it is not available through other means.
template <typename Dst = T, class CheckHandler = CheckOnFailure>
constexpr StrictNumeric<Dst> ValueOrDie() const {
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
? static_cast<Dst>(state_.value())
: CheckHandler::template HandleFailure<Dst>();
}
// ValueOrDefault(T default_value) - A convenience method that returns the
// current value if the state is valid, and the supplied default_value for
// any other state.
// A range checked destination type can be supplied using the Dst template
// parameter. WARNING: This function may fail to compile or CHECK at runtime
// if the supplied default_value is not within range of the destination type.
template <typename Dst = T, typename Src>
constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
return BASE_NUMERICS_LIKELY(IsValid<Dst>())
? static_cast<Dst>(state_.value())
: checked_cast<Dst>(default_value);
}
// Returns a checked numeric of the specified type, cast from the current
// CheckedNumeric. If the current state is invalid or the destination cannot
// represent the result then the returned CheckedNumeric will be invalid.
template <typename Dst>
constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
return *this;
}
// This friend method is available solely for providing more detailed logging
// in the the tests. Do not implement it in production code, because the
// underlying values may change at any time.
template <typename U>
friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
// Prototypes for the supported arithmetic operator overloads.
template <typename Src>
constexpr CheckedNumeric& operator+=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator-=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator*=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator/=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator%=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator<<=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator>>=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator&=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator|=(const Src rhs);
template <typename Src>
constexpr CheckedNumeric& operator^=(const Src rhs);
constexpr CheckedNumeric operator-() const {
// The negation of two's complement int min is int min, so we simply
// check for that in the constexpr case.
// We use an optimized code path for a known run-time variable.
return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
std::is_floating_point<T>::value
? CheckedNumeric<T>(
NegateWrapper(state_.value()),
IsValid() && (!std::is_signed<T>::value ||
std::is_floating_point<T>::value ||
NegateWrapper(state_.value()) !=
std::numeric_limits<T>::lowest()))
: FastRuntimeNegate();
}
constexpr CheckedNumeric operator~() const {
return CheckedNumeric<decltype(InvertWrapper(T()))>(
InvertWrapper(state_.value()), IsValid());
}
constexpr CheckedNumeric Abs() const {
return !IsValueNegative(state_.value()) ? *this : -*this;
}
template <typename U>
constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
const U rhs) const {
using R = typename UnderlyingType<U>::type;
using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
// TODO(jschuh): This can be converted to the MathOp version and remain
// constexpr once we have C++14 support.
return CheckedNumeric<result_type>(
static_cast<result_type>(
IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
? state_.value()
: Wrapper<U>::value(rhs)),
state_.is_valid() && Wrapper<U>::is_valid(rhs));
}
template <typename U>
constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
const U rhs) const {
using R = typename UnderlyingType<U>::type;
using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
// TODO(jschuh): This can be converted to the MathOp version and remain
// constexpr once we have C++14 support.
return CheckedNumeric<result_type>(
static_cast<result_type>(
IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
? state_.value()
: Wrapper<U>::value(rhs)),
state_.is_valid() && Wrapper<U>::is_valid(rhs));
}
// This function is available only for integral types. It returns an unsigned
// integer of the same width as the source type, containing the absolute value
// of the source, and properly handling signed min.
constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
UnsignedAbs() const {
return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
SafeUnsignedAbs(state_.value()), state_.is_valid());
}
constexpr CheckedNumeric& operator++() {
*this += 1;
return *this;
}
constexpr CheckedNumeric operator++(int) {
CheckedNumeric value = *this;
*this += 1;
return value;
}
constexpr CheckedNumeric& operator--() {
*this -= 1;
return *this;
}
constexpr CheckedNumeric operator--(int) {
CheckedNumeric value = *this;
*this -= 1;
return value;
}
// These perform the actual math operations on the CheckedNumerics.
// Binary arithmetic operations.
template <template <typename, typename, typename> class M,
typename L,
typename R>
static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
using Math = typename MathWrapper<M, L, R>::math;
T result = 0;
bool is_valid =
Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
return CheckedNumeric<T>(result, is_valid);
}
// Assignment arithmetic operations.
template <template <typename, typename, typename> class M, typename R>
constexpr CheckedNumeric& MathOp(const R rhs) {
using Math = typename MathWrapper<M, T, R>::math;
T result = 0; // Using T as the destination saves a range check.
bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
*this = CheckedNumeric<T>(result, is_valid);
return *this;
}
private:
CheckedNumericState<T> state_;
CheckedNumeric FastRuntimeNegate() const {
T result;
bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
return CheckedNumeric<T>(result, IsValid() && success);
}
template <typename Src>
constexpr CheckedNumeric(Src value, bool is_valid)
: state_(value, is_valid) {}
// These wrappers allow us to handle state the same way for both
// CheckedNumeric and POD arithmetic types.
template <typename Src>
struct Wrapper {
static constexpr bool is_valid(Src) { return true; }
static constexpr Src value(Src value) { return value; }
};
template <typename Src>
struct Wrapper<CheckedNumeric<Src>> {
static constexpr bool is_valid(const CheckedNumeric<Src> v) {
return v.IsValid();
}
static constexpr Src value(const CheckedNumeric<Src> v) {
return v.state_.value();
}
};
template <typename Src>
struct Wrapper<StrictNumeric<Src>> {
static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
static constexpr Src value(const StrictNumeric<Src> v) {
return static_cast<Src>(v);
}
};
};
// Convenience functions to avoid the ugly template disambiguator syntax.
template <typename Dst, typename Src>
constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
return value.template IsValid<Dst>();
}
template <typename Dst, typename Src>
constexpr StrictNumeric<Dst> ValueOrDieForType(
const CheckedNumeric<Src> value) {
return value.template ValueOrDie<Dst>();
}
template <typename Dst, typename Src, typename Default>
constexpr StrictNumeric<Dst> ValueOrDefaultForType(
const CheckedNumeric<Src> value,
const Default default_value) {
return value.template ValueOrDefault<Dst>(default_value);
}
// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
// or CheckedNumericType.
template <typename T>
constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
const T value) {
return value;
}
// These implement the variadic wrapper for the math operations.
template <template <typename, typename, typename> class M,
typename L,
typename R>
constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
const L lhs,
const R rhs) {
using Math = typename MathWrapper<M, L, R>::math;
return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
rhs);
}
// General purpose wrapper template for arithmetic operations.
template <template <typename, typename, typename> class M,
typename L,
typename R,
typename... Args>
constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
CheckMathOp(const L lhs, const R rhs, const Args... args) {
return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
}
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
// These are some extra StrictNumeric operators to support simple pointer
// arithmetic with our result types. Since wrapping on a pointer is always
// bad, we trigger the CHECK condition here.
template <typename L, typename R>
L* operator+(L* lhs, const StrictNumeric<R> rhs) {
uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
CheckMul(sizeof(L), static_cast<R>(rhs)))
.template ValueOrDie<uintptr_t>();
return reinterpret_cast<L*>(result);
}
template <typename L, typename R>
L* operator-(L* lhs, const StrictNumeric<R> rhs) {
uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
CheckMul(sizeof(L), static_cast<R>(rhs)))
.template ValueOrDie<uintptr_t>();
return reinterpret_cast<L*>(result);
}
} // namespace internal
using internal::CheckedNumeric;
using internal::IsValidForType;
using internal::ValueOrDieForType;
using internal::ValueOrDefaultForType;
using internal::MakeCheckedNum;
using internal::CheckMax;
using internal::CheckMin;
using internal::CheckAdd;
using internal::CheckSub;
using internal::CheckMul;
using internal::CheckDiv;
using internal::CheckMod;
using internal::CheckLsh;
using internal::CheckRsh;
using internal::CheckAnd;
using internal::CheckOr;
using internal::CheckXor;
} // namespace base
#endif // BASE_NUMERICS_CHECKED_MATH_H_

View File

@@ -0,0 +1,567 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_
#define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
#include <stddef.h>
#include <stdint.h>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <type_traits>
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math_shared_impl.h"
namespace base {
namespace internal {
template <typename T>
constexpr bool CheckedAddImpl(T x, T y, T* result) {
static_assert(std::is_integral<T>::value, "Type must be integral");
// Since the value of x+y is undefined if we have a signed type, we compute
// it using the unsigned type of the same size.
using UnsignedDst = typename std::make_unsigned<T>::type;
using SignedDst = typename std::make_signed<T>::type;
UnsignedDst ux = static_cast<UnsignedDst>(x);
UnsignedDst uy = static_cast<UnsignedDst>(y);
UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
*result = static_cast<T>(uresult);
// Addition is valid if the sign of (x + y) is equal to either that of x or
// that of y.
return (std::is_signed<T>::value)
? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
: uresult >= uy; // Unsigned is either valid or underflow.
}
template <typename T, typename U, class Enable = void>
struct CheckedAddOp {};
template <typename T, typename U>
struct CheckedAddOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
// TODO(jschuh) Make this "constexpr if" once we're C++17.
if (CheckedAddFastOp<T, U>::is_supported)
return CheckedAddFastOp<T, U>::Do(x, y, result);
// Double the underlying type up to a full machine word.
using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
using Promotion =
typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
IntegerBitsPlusSign<intptr_t>::value),
typename BigEnoughPromotion<T, U>::type,
FastPromotion>::type;
// Fail if either operand is out of range for the promoted type.
// TODO(jschuh): This could be made to work for a broader range of values.
if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
!IsValueInRangeForNumericType<Promotion>(y))) {
return false;
}
Promotion presult = {};
bool is_valid = true;
if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
} else {
is_valid = CheckedAddImpl(static_cast<Promotion>(x),
static_cast<Promotion>(y), &presult);
}
*result = static_cast<V>(presult);
return is_valid && IsValueInRangeForNumericType<V>(presult);
}
};
template <typename T>
constexpr bool CheckedSubImpl(T x, T y, T* result) {
static_assert(std::is_integral<T>::value, "Type must be integral");
// Since the value of x+y is undefined if we have a signed type, we compute
// it using the unsigned type of the same size.
using UnsignedDst = typename std::make_unsigned<T>::type;
using SignedDst = typename std::make_signed<T>::type;
UnsignedDst ux = static_cast<UnsignedDst>(x);
UnsignedDst uy = static_cast<UnsignedDst>(y);
UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
*result = static_cast<T>(uresult);
// Subtraction is valid if either x and y have same sign, or (x-y) and x have
// the same sign.
return (std::is_signed<T>::value)
? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
: x >= y;
}
template <typename T, typename U, class Enable = void>
struct CheckedSubOp {};
template <typename T, typename U>
struct CheckedSubOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
// TODO(jschuh) Make this "constexpr if" once we're C++17.
if (CheckedSubFastOp<T, U>::is_supported)
return CheckedSubFastOp<T, U>::Do(x, y, result);
// Double the underlying type up to a full machine word.
using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
using Promotion =
typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
IntegerBitsPlusSign<intptr_t>::value),
typename BigEnoughPromotion<T, U>::type,
FastPromotion>::type;
// Fail if either operand is out of range for the promoted type.
// TODO(jschuh): This could be made to work for a broader range of values.
if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
!IsValueInRangeForNumericType<Promotion>(y))) {
return false;
}
Promotion presult = {};
bool is_valid = true;
if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
} else {
is_valid = CheckedSubImpl(static_cast<Promotion>(x),
static_cast<Promotion>(y), &presult);
}
*result = static_cast<V>(presult);
return is_valid && IsValueInRangeForNumericType<V>(presult);
}
};
template <typename T>
constexpr bool CheckedMulImpl(T x, T y, T* result) {
static_assert(std::is_integral<T>::value, "Type must be integral");
// Since the value of x*y is potentially undefined if we have a signed type,
// we compute it using the unsigned type of the same size.
using UnsignedDst = typename std::make_unsigned<T>::type;
using SignedDst = typename std::make_signed<T>::type;
const UnsignedDst ux = SafeUnsignedAbs(x);
const UnsignedDst uy = SafeUnsignedAbs(y);
UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
const bool is_negative =
std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
*result = is_negative ? 0 - uresult : uresult;
// We have a fast out for unsigned identity or zero on the second operand.
// After that it's an unsigned overflow check on the absolute value, with
// a +1 bound for a negative result.
return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
}
template <typename T, typename U, class Enable = void>
struct CheckedMulOp {};
template <typename T, typename U>
struct CheckedMulOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
// TODO(jschuh) Make this "constexpr if" once we're C++17.
if (CheckedMulFastOp<T, U>::is_supported)
return CheckedMulFastOp<T, U>::Do(x, y, result);
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
// Verify the destination type can hold the result (always true for 0).
if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
!IsValueInRangeForNumericType<Promotion>(y)) &&
x && y)) {
return false;
}
Promotion presult = {};
bool is_valid = true;
if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
// The fast op may be available with the promoted type.
is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(x, y, &presult);
} else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
} else {
is_valid = CheckedMulImpl(static_cast<Promotion>(x),
static_cast<Promotion>(y), &presult);
}
*result = static_cast<V>(presult);
return is_valid && IsValueInRangeForNumericType<V>(presult);
}
};
// Division just requires a check for a zero denominator or an invalid negation
// on signed min/-1.
template <typename T, typename U, class Enable = void>
struct CheckedDivOp {};
template <typename T, typename U>
struct CheckedDivOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
if (BASE_NUMERICS_UNLIKELY(!y))
return false;
// The overflow check can be compiled away if we don't have the exact
// combination of types needed to trigger this case.
using Promotion = typename BigEnoughPromotion<T, U>::type;
if (BASE_NUMERICS_UNLIKELY(
(std::is_signed<T>::value && std::is_signed<U>::value &&
IsTypeInRangeForNumericType<T, Promotion>::value &&
static_cast<Promotion>(x) ==
std::numeric_limits<Promotion>::lowest() &&
y == static_cast<U>(-1)))) {
return false;
}
// This branch always compiles away if the above branch wasn't removed.
if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
!IsValueInRangeForNumericType<Promotion>(y)) &&
x)) {
return false;
}
Promotion presult = Promotion(x) / Promotion(y);
*result = static_cast<V>(presult);
return IsValueInRangeForNumericType<V>(presult);
}
};
template <typename T, typename U, class Enable = void>
struct CheckedModOp {};
template <typename T, typename U>
struct CheckedModOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
using Promotion = typename BigEnoughPromotion<T, U>::type;
if (BASE_NUMERICS_LIKELY(y)) {
Promotion presult = static_cast<Promotion>(x) % static_cast<Promotion>(y);
*result = static_cast<Promotion>(presult);
return IsValueInRangeForNumericType<V>(presult);
}
return false;
}
};
template <typename T, typename U, class Enable = void>
struct CheckedLshOp {};
// Left shift. Shifts less than 0 or greater than or equal to the number
// of bits in the promoted type are undefined. Shifts of negative values
// are undefined. Otherwise it is defined when the result fits.
template <typename T, typename U>
struct CheckedLshOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = T;
template <typename V>
static constexpr bool Do(T x, U shift, V* result) {
// Disallow negative numbers and verify the shift is in bounds.
if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
as_unsigned(shift) <
as_unsigned(std::numeric_limits<T>::digits))) {
// Shift as unsigned to avoid undefined behavior.
*result = static_cast<V>(as_unsigned(x) << shift);
// If the shift can be reversed, we know it was valid.
return *result >> shift == x;
}
// Handle the legal corner-case of a full-width signed shift of zero.
return std::is_signed<T>::value && !x &&
as_unsigned(shift) == as_unsigned(std::numeric_limits<T>::digits);
}
};
template <typename T, typename U, class Enable = void>
struct CheckedRshOp {};
// Right shift. Shifts less than 0 or greater than or equal to the number
// of bits in the promoted type are undefined. Otherwise, it is always defined,
// but a right shift of a negative value is implementation-dependent.
template <typename T, typename U>
struct CheckedRshOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = T;
template <typename V>
static bool Do(T x, U shift, V* result) {
// Use the type conversion push negative values out of range.
if (BASE_NUMERICS_LIKELY(as_unsigned(shift) <
IntegerBitsPlusSign<T>::value)) {
T tmp = x >> shift;
*result = static_cast<V>(tmp);
return IsValueInRangeForNumericType<V>(tmp);
}
return false;
}
};
template <typename T, typename U, class Enable = void>
struct CheckedAndOp {};
// For simplicity we support only unsigned integer results.
template <typename T, typename U>
struct CheckedAndOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename std::make_unsigned<
typename MaxExponentPromotion<T, U>::type>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
*result = static_cast<V>(tmp);
return IsValueInRangeForNumericType<V>(tmp);
}
};
template <typename T, typename U, class Enable = void>
struct CheckedOrOp {};
// For simplicity we support only unsigned integers.
template <typename T, typename U>
struct CheckedOrOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename std::make_unsigned<
typename MaxExponentPromotion<T, U>::type>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
*result = static_cast<V>(tmp);
return IsValueInRangeForNumericType<V>(tmp);
}
};
template <typename T, typename U, class Enable = void>
struct CheckedXorOp {};
// For simplicity we support only unsigned integers.
template <typename T, typename U>
struct CheckedXorOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename std::make_unsigned<
typename MaxExponentPromotion<T, U>::type>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
*result = static_cast<V>(tmp);
return IsValueInRangeForNumericType<V>(tmp);
}
};
// Max doesn't really need to be implemented this way because it can't fail,
// but it makes the code much cleaner to use the MathOp wrappers.
template <typename T, typename U, class Enable = void>
struct CheckedMaxOp {};
template <typename T, typename U>
struct CheckedMaxOp<
T,
U,
typename std::enable_if<std::is_arithmetic<T>::value &&
std::is_arithmetic<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
result_type tmp = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
: static_cast<result_type>(y);
*result = static_cast<V>(tmp);
return IsValueInRangeForNumericType<V>(tmp);
}
};
// Min doesn't really need to be implemented this way because it can't fail,
// but it makes the code much cleaner to use the MathOp wrappers.
template <typename T, typename U, class Enable = void>
struct CheckedMinOp {};
template <typename T, typename U>
struct CheckedMinOp<
T,
U,
typename std::enable_if<std::is_arithmetic<T>::value &&
std::is_arithmetic<U>::value>::type> {
using result_type = typename LowestValuePromotion<T, U>::type;
template <typename V>
static constexpr bool Do(T x, U y, V* result) {
result_type tmp = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
: static_cast<result_type>(y);
*result = static_cast<V>(tmp);
return IsValueInRangeForNumericType<V>(tmp);
}
};
// This is just boilerplate that wraps the standard floating point arithmetic.
// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
template <typename T, typename U> \
struct Checked##NAME##Op< \
T, U, \
typename std::enable_if<std::is_floating_point<T>::value || \
std::is_floating_point<U>::value>::type> { \
using result_type = typename MaxExponentPromotion<T, U>::type; \
template <typename V> \
static constexpr bool Do(T x, U y, V* result) { \
using Promotion = typename MaxExponentPromotion<T, U>::type; \
Promotion presult = x OP y; \
*result = static_cast<V>(presult); \
return IsValueInRangeForNumericType<V>(presult); \
} \
};
BASE_FLOAT_ARITHMETIC_OPS(Add, +)
BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
BASE_FLOAT_ARITHMETIC_OPS(Div, /)
#undef BASE_FLOAT_ARITHMETIC_OPS
// Floats carry around their validity state with them, but integers do not. So,
// we wrap the underlying value in a specialization in order to hide that detail
// and expose an interface via accessors.
enum NumericRepresentation {
NUMERIC_INTEGER,
NUMERIC_FLOATING,
NUMERIC_UNKNOWN
};
template <typename NumericType>
struct GetNumericRepresentation {
static const NumericRepresentation value =
std::is_integral<NumericType>::value
? NUMERIC_INTEGER
: (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
: NUMERIC_UNKNOWN);
};
template <typename T,
NumericRepresentation type = GetNumericRepresentation<T>::value>
class CheckedNumericState {};
// Integrals require quite a bit of additional housekeeping to manage state.
template <typename T>
class CheckedNumericState<T, NUMERIC_INTEGER> {
private:
// is_valid_ precedes value_ because member intializers in the constructors
// are evaluated in field order, and is_valid_ must be read when initializing
// value_.
bool is_valid_;
T value_;
// Ensures that a type conversion does not trigger undefined behavior.
template <typename Src>
static constexpr T WellDefinedConversionOrZero(const Src value,
const bool is_valid) {
using SrcType = typename internal::UnderlyingType<Src>::type;
return (std::is_integral<SrcType>::value || is_valid)
? static_cast<T>(value)
: static_cast<T>(0);
}
public:
template <typename Src, NumericRepresentation type>
friend class CheckedNumericState;
constexpr CheckedNumericState() : is_valid_(true), value_(0) {}
template <typename Src>
constexpr CheckedNumericState(Src value, bool is_valid)
: is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
value_(WellDefinedConversionOrZero(value, is_valid_)) {
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
}
// Copy constructor.
template <typename Src>
constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
: is_valid_(rhs.IsValid()),
value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
template <typename Src>
constexpr explicit CheckedNumericState(Src value)
: is_valid_(IsValueInRangeForNumericType<T>(value)),
value_(WellDefinedConversionOrZero(value, is_valid_)) {}
constexpr bool is_valid() const { return is_valid_; }
constexpr T value() const { return value_; }
};
// Floating points maintain their own validity, but need translation wrappers.
template <typename T>
class CheckedNumericState<T, NUMERIC_FLOATING> {
private:
T value_;
// Ensures that a type conversion does not trigger undefined behavior.
template <typename Src>
static constexpr T WellDefinedConversionOrNaN(const Src value,
const bool is_valid) {
using SrcType = typename internal::UnderlyingType<Src>::type;
return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
NUMERIC_RANGE_CONTAINED ||
is_valid)
? static_cast<T>(value)
: std::numeric_limits<T>::quiet_NaN();
}
public:
template <typename Src, NumericRepresentation type>
friend class CheckedNumericState;
constexpr CheckedNumericState() : value_(0.0) {}
template <typename Src>
constexpr CheckedNumericState(Src value, bool is_valid)
: value_(WellDefinedConversionOrNaN(value, is_valid)) {}
template <typename Src>
constexpr explicit CheckedNumericState(Src value)
: value_(WellDefinedConversionOrNaN(
value,
IsValueInRangeForNumericType<T>(value))) {}
// Copy constructor.
template <typename Src>
constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
: value_(WellDefinedConversionOrNaN(
rhs.value(),
rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
constexpr bool is_valid() const {
// Written this way because std::isfinite is not reliably constexpr.
return MustTreatAsConstexpr(value_)
? value_ <= std::numeric_limits<T>::max() &&
value_ >= std::numeric_limits<T>::lowest()
: std::isfinite(value_);
}
constexpr T value() const { return value_; }
};
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_

View File

@@ -0,0 +1,264 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_CLAMPED_MATH_H_
#define BASE_NUMERICS_CLAMPED_MATH_H_
#include <stddef.h>
#include <limits>
#include <type_traits>
#include "base/numerics/clamped_math_impl.h"
namespace base {
namespace internal {
template <typename T>
class ClampedNumeric {
static_assert(std::is_arithmetic<T>::value,
"ClampedNumeric<T>: T must be a numeric type.");
public:
using type = T;
constexpr ClampedNumeric() : value_(0) {}
// Copy constructor.
template <typename Src>
constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
: value_(saturated_cast<T>(rhs.value_)) {}
template <typename Src>
friend class ClampedNumeric;
// This is not an explicit constructor because we implicitly upgrade regular
// numerics to ClampedNumerics to make them easier to use.
template <typename Src>
constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit)
: value_(saturated_cast<T>(value)) {
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
}
// This is not an explicit constructor because we want a seamless conversion
// from StrictNumeric types.
template <typename Src>
constexpr ClampedNumeric(
StrictNumeric<Src> value) // NOLINT(runtime/explicit)
: value_(saturated_cast<T>(static_cast<Src>(value))) {}
// Returns a ClampedNumeric of the specified type, cast from the current
// ClampedNumeric, and saturated to the destination type.
template <typename Dst>
constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
return *this;
}
// Prototypes for the supported arithmetic operator overloads.
template <typename Src>
constexpr ClampedNumeric& operator+=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator-=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator*=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator/=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator%=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator<<=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator>>=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator&=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator|=(const Src rhs);
template <typename Src>
constexpr ClampedNumeric& operator^=(const Src rhs);
constexpr ClampedNumeric operator-() const {
// The negation of two's complement int min is int min, so that's the
// only overflow case where we will saturate.
return ClampedNumeric<T>(SaturatedNegWrapper(value_));
}
constexpr ClampedNumeric operator~() const {
return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));
}
constexpr ClampedNumeric Abs() const {
// The negation of two's complement int min is int min, so that's the
// only overflow case where we will saturate.
return ClampedNumeric<T>(SaturatedAbsWrapper(value_));
}
template <typename U>
constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
const U rhs) const {
using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
return ClampedNumeric<result_type>(
ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
}
template <typename U>
constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
const U rhs) const {
using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
return ClampedNumeric<result_type>(
ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
}
// This function is available only for integral types. It returns an unsigned
// integer of the same width as the source type, containing the absolute value
// of the source, and properly handling signed min.
constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>
UnsignedAbs() const {
return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(
SafeUnsignedAbs(value_));
}
constexpr ClampedNumeric& operator++() {
*this += 1;
return *this;
}
constexpr ClampedNumeric operator++(int) {
ClampedNumeric value = *this;
*this += 1;
return value;
}
constexpr ClampedNumeric& operator--() {
*this -= 1;
return *this;
}
constexpr ClampedNumeric operator--(int) {
ClampedNumeric value = *this;
*this -= 1;
return value;
}
// These perform the actual math operations on the ClampedNumerics.
// Binary arithmetic operations.
template <template <typename, typename, typename> class M,
typename L,
typename R>
static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {
using Math = typename MathWrapper<M, L, R>::math;
return ClampedNumeric<T>(
Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
}
// Assignment arithmetic operations.
template <template <typename, typename, typename> class M, typename R>
constexpr ClampedNumeric& MathOp(const R rhs) {
using Math = typename MathWrapper<M, T, R>::math;
*this =
ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
return *this;
}
template <typename Dst>
constexpr operator Dst() const {
return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
value_);
}
// This method extracts the raw integer value without saturating it to the
// destination type as the conversion operator does. This is useful when
// e.g. assigning to an auto type or passing as a deduced template parameter.
constexpr T RawValue() const { return value_; }
private:
T value_;
// These wrappers allow us to handle state the same way for both
// ClampedNumeric and POD arithmetic types.
template <typename Src>
struct Wrapper {
static constexpr Src value(Src value) {
return static_cast<typename UnderlyingType<Src>::type>(value);
}
};
};
// Convience wrapper to return a new ClampedNumeric from the provided arithmetic
// or ClampedNumericType.
template <typename T>
constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
const T value) {
return value;
}
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
// Overload the ostream output operator to make logging work nicely.
template <typename T>
std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
os << static_cast<T>(value);
return os;
}
#endif
// These implement the variadic wrapper for the math operations.
template <template <typename, typename, typename> class M,
typename L,
typename R>
constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(
const L lhs,
const R rhs) {
using Math = typename MathWrapper<M, L, R>::math;
return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
rhs);
}
// General purpose wrapper template for arithmetic operations.
template <template <typename, typename, typename> class M,
typename L,
typename R,
typename... Args>
constexpr ClampedNumeric<typename ResultType<M, L, R, Args...>::type>
ClampMathOp(const L lhs, const R rhs, const Args... args) {
return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
}
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <)
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=)
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >)
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=)
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==)
BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=)
} // namespace internal
using internal::ClampedNumeric;
using internal::MakeClampedNum;
using internal::ClampMax;
using internal::ClampMin;
using internal::ClampAdd;
using internal::ClampSub;
using internal::ClampMul;
using internal::ClampDiv;
using internal::ClampMod;
using internal::ClampLsh;
using internal::ClampRsh;
using internal::ClampAnd;
using internal::ClampOr;
using internal::ClampXor;
} // namespace base
#endif // BASE_NUMERICS_CLAMPED_MATH_H_

View File

@@ -0,0 +1,341 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
#include <stddef.h>
#include <stdint.h>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <type_traits>
#include "base/numerics/checked_math.h"
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math_shared_impl.h"
namespace base {
namespace internal {
template <typename T,
typename std::enable_if<std::is_integral<T>::value &&
std::is_signed<T>::value>::type* = nullptr>
constexpr T SaturatedNegWrapper(T value) {
return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported
? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
? NegateWrapper(value)
: std::numeric_limits<T>::max())
: ClampedNegFastOp<T>::Do(value);
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value &&
!std::is_signed<T>::value>::type* = nullptr>
constexpr T SaturatedNegWrapper(T value) {
return T(0);
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T SaturatedNegWrapper(T value) {
return -value;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr T SaturatedAbsWrapper(T value) {
// The calculation below is a static identity for unsigned types, but for
// signed integer types it provides a non-branching, saturated absolute value.
// This works because SafeUnsignedAbs() returns an unsigned type, which can
// represent the absolute value of all negative numbers of an equal-width
// integer type. The call to IsValueNegative() then detects overflow in the
// special case of numeric_limits<T>::min(), by evaluating the bit pattern as
// a signed integer value. If it is the overflow case, we end up subtracting
// one from the unsigned result, thus saturating to numeric_limits<T>::max().
return static_cast<T>(SafeUnsignedAbs(value) -
IsValueNegative<T>(SafeUnsignedAbs(value)));
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T SaturatedAbsWrapper(T value) {
return value < 0 ? -value : value;
}
template <typename T, typename U, class Enable = void>
struct ClampedAddOp {};
template <typename T, typename U>
struct ClampedAddOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V = result_type>
static constexpr V Do(T x, U y) {
if (ClampedAddFastOp<T, U>::is_supported)
return ClampedAddFastOp<T, U>::template Do<V>(x, y);
static_assert(std::is_same<V, result_type>::value ||
IsTypeInRangeForNumericType<U, V>::value,
"The saturation result cannot be determined from the "
"provided types.");
const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
V result = {};
return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
? result
: saturated;
}
};
template <typename T, typename U, class Enable = void>
struct ClampedSubOp {};
template <typename T, typename U>
struct ClampedSubOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V = result_type>
static constexpr V Do(T x, U y) {
// TODO(jschuh) Make this "constexpr if" once we're C++17.
if (ClampedSubFastOp<T, U>::is_supported)
return ClampedSubFastOp<T, U>::template Do<V>(x, y);
static_assert(std::is_same<V, result_type>::value ||
IsTypeInRangeForNumericType<U, V>::value,
"The saturation result cannot be determined from the "
"provided types.");
const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
V result = {};
return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
? result
: saturated;
}
};
template <typename T, typename U, class Enable = void>
struct ClampedMulOp {};
template <typename T, typename U>
struct ClampedMulOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V = result_type>
static constexpr V Do(T x, U y) {
// TODO(jschuh) Make this "constexpr if" once we're C++17.
if (ClampedMulFastOp<T, U>::is_supported)
return ClampedMulFastOp<T, U>::template Do<V>(x, y);
V result = {};
const V saturated =
CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
? result
: saturated;
}
};
template <typename T, typename U, class Enable = void>
struct ClampedDivOp {};
template <typename T, typename U>
struct ClampedDivOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V = result_type>
static constexpr V Do(T x, U y) {
V result = {};
if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
return result;
// Saturation goes to max, min, or NaN (if x is zero).
return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
: SaturationDefaultLimits<V>::NaN();
}
};
template <typename T, typename U, class Enable = void>
struct ClampedModOp {};
template <typename T, typename U>
struct ClampedModOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V = result_type>
static constexpr V Do(T x, U y) {
V result = {};
return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
? result
: x;
}
};
template <typename T, typename U, class Enable = void>
struct ClampedLshOp {};
// Left shift. Non-zero values saturate in the direction of the sign. A zero
// shifted by any value always results in zero.
template <typename T, typename U>
struct ClampedLshOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = T;
template <typename V = result_type>
static constexpr V Do(T x, U shift) {
static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
// Shift as unsigned to avoid undefined behavior.
V result = static_cast<V>(as_unsigned(x) << shift);
// If the shift can be reversed, we know it was valid.
if (BASE_NUMERICS_LIKELY(result >> shift == x))
return result;
}
return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
}
};
template <typename T, typename U, class Enable = void>
struct ClampedRshOp {};
// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
template <typename T, typename U>
struct ClampedRshOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = T;
template <typename V = result_type>
static constexpr V Do(T x, U shift) {
static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
// Signed right shift is odd, because it saturates to -1 or 0.
const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
? saturated_cast<V>(x >> shift)
: saturated;
}
};
template <typename T, typename U, class Enable = void>
struct ClampedAndOp {};
template <typename T, typename U>
struct ClampedAndOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename std::make_unsigned<
typename MaxExponentPromotion<T, U>::type>::type;
template <typename V>
static constexpr V Do(T x, U y) {
return static_cast<result_type>(x) & static_cast<result_type>(y);
}
};
template <typename T, typename U, class Enable = void>
struct ClampedOrOp {};
// For simplicity we promote to unsigned integers.
template <typename T, typename U>
struct ClampedOrOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename std::make_unsigned<
typename MaxExponentPromotion<T, U>::type>::type;
template <typename V>
static constexpr V Do(T x, U y) {
return static_cast<result_type>(x) | static_cast<result_type>(y);
}
};
template <typename T, typename U, class Enable = void>
struct ClampedXorOp {};
// For simplicity we support only unsigned integers.
template <typename T, typename U>
struct ClampedXorOp<T,
U,
typename std::enable_if<std::is_integral<T>::value &&
std::is_integral<U>::value>::type> {
using result_type = typename std::make_unsigned<
typename MaxExponentPromotion<T, U>::type>::type;
template <typename V>
static constexpr V Do(T x, U y) {
return static_cast<result_type>(x) ^ static_cast<result_type>(y);
}
};
template <typename T, typename U, class Enable = void>
struct ClampedMaxOp {};
template <typename T, typename U>
struct ClampedMaxOp<
T,
U,
typename std::enable_if<std::is_arithmetic<T>::value &&
std::is_arithmetic<U>::value>::type> {
using result_type = typename MaxExponentPromotion<T, U>::type;
template <typename V = result_type>
static constexpr V Do(T x, U y) {
return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
: saturated_cast<V>(y);
}
};
template <typename T, typename U, class Enable = void>
struct ClampedMinOp {};
template <typename T, typename U>
struct ClampedMinOp<
T,
U,
typename std::enable_if<std::is_arithmetic<T>::value &&
std::is_arithmetic<U>::value>::type> {
using result_type = typename LowestValuePromotion<T, U>::type;
template <typename V = result_type>
static constexpr V Do(T x, U y) {
return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
: saturated_cast<V>(y);
}
};
// This is just boilerplate that wraps the standard floating point arithmetic.
// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
template <typename T, typename U> \
struct Clamped##NAME##Op< \
T, U, \
typename std::enable_if<std::is_floating_point<T>::value || \
std::is_floating_point<U>::value>::type> { \
using result_type = typename MaxExponentPromotion<T, U>::type; \
template <typename V = result_type> \
static constexpr V Do(T x, U y) { \
return saturated_cast<V>(x OP y); \
} \
};
BASE_FLOAT_ARITHMETIC_OPS(Add, +)
BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
BASE_FLOAT_ARITHMETIC_OPS(Div, /)
#undef BASE_FLOAT_ARITHMETIC_OPS
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_

View File

@@ -0,0 +1,19 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_MATH_CONSTANTS_H_
#define BASE_NUMERICS_MATH_CONSTANTS_H_
namespace base {
constexpr double kPiDouble = 3.14159265358979323846;
constexpr float kPiFloat = 3.14159265358979323846f;
// The mean acceleration due to gravity on Earth in m/s^2.
constexpr double kMeanGravityDouble = 9.80665;
constexpr float kMeanGravityFloat = 9.80665f;
} // namespace base
#endif // BASE_NUMERICS_MATH_CONSTANTS_H_

View File

@@ -0,0 +1,27 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_RANGES_H_
#define BASE_NUMERICS_RANGES_H_
#include <algorithm>
#include <cmath>
namespace base {
// To be replaced with std::clamp() from C++17, someday.
template <class T>
constexpr const T& ClampToRange(const T& value, const T& min, const T& max) {
return std::min(std::max(value, min), max);
}
template <typename T>
constexpr bool IsApproximatelyEqual(T lhs, T rhs, T tolerance) {
static_assert(std::is_arithmetic<T>::value, "Argument must be arithmetic");
return std::abs(rhs - lhs) <= tolerance;
}
} // namespace base
#endif // BASE_NUMERICS_RANGES_H_

View File

@@ -0,0 +1,358 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_
#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
#include <stddef.h>
#include <limits>
#include <type_traits>
#include "base/numerics/safe_conversions_impl.h"
#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
#include "base/numerics/safe_conversions_arm_impl.h"
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
#else
#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
#endif
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
#include <ostream>
#endif
namespace base {
namespace internal {
#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
template <typename Dst, typename Src>
struct SaturateFastAsmOp {
static const bool is_supported = false;
static constexpr Dst Do(Src) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<Dst>();
}
};
#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
// The following special case a few specific integer conversions where we can
// eke out better performance than range checking.
template <typename Dst, typename Src, typename Enable = void>
struct IsValueInRangeFastOp {
static const bool is_supported = false;
static constexpr bool Do(Src value) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<bool>();
}
};
// Signed to signed range comparison.
template <typename Dst, typename Src>
struct IsValueInRangeFastOp<
Dst,
Src,
typename std::enable_if<
std::is_integral<Dst>::value && std::is_integral<Src>::value &&
std::is_signed<Dst>::value && std::is_signed<Src>::value &&
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
static const bool is_supported = true;
static constexpr bool Do(Src value) {
// Just downcast to the smaller type, sign extend it back to the original
// type, and then see if it matches the original value.
return value == static_cast<Dst>(value);
}
};
// Signed to unsigned range comparison.
template <typename Dst, typename Src>
struct IsValueInRangeFastOp<
Dst,
Src,
typename std::enable_if<
std::is_integral<Dst>::value && std::is_integral<Src>::value &&
!std::is_signed<Dst>::value && std::is_signed<Src>::value &&
!IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
static const bool is_supported = true;
static constexpr bool Do(Src value) {
// We cast a signed as unsigned to overflow negative values to the top,
// then compare against whichever maximum is smaller, as our upper bound.
return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
}
};
// Convenience function that returns true if the supplied value is in range
// for the destination type.
template <typename Dst, typename Src>
constexpr bool IsValueInRangeForNumericType(Src value) {
using SrcType = typename internal::UnderlyingType<Src>::type;
return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
static_cast<SrcType>(value))
: internal::DstRangeRelationToSrcRange<Dst>(
static_cast<SrcType>(value))
.IsValid();
}
// checked_cast<> is analogous to static_cast<> for numeric types,
// except that it CHECKs that the specified numeric conversion will not
// overflow or underflow. NaN source will always trigger a CHECK.
template <typename Dst,
class CheckHandler = internal::CheckOnFailure,
typename Src>
constexpr Dst checked_cast(Src value) {
// This throws a compile-time error on evaluating the constexpr if it can be
// determined at compile-time as failing, otherwise it will CHECK at runtime.
using SrcType = typename internal::UnderlyingType<Src>::type;
return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
? static_cast<Dst>(static_cast<SrcType>(value))
: CheckHandler::template HandleFailure<Dst>();
}
// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
// You may provide your own limits (e.g. to saturated_cast) so long as you
// implement all of the static constexpr member functions in the class below.
template <typename T>
struct SaturationDefaultLimits : public std::numeric_limits<T> {
static constexpr T NaN() {
return std::numeric_limits<T>::has_quiet_NaN
? std::numeric_limits<T>::quiet_NaN()
: T();
}
using std::numeric_limits<T>::max;
static constexpr T Overflow() {
return std::numeric_limits<T>::has_infinity
? std::numeric_limits<T>::infinity()
: std::numeric_limits<T>::max();
}
using std::numeric_limits<T>::lowest;
static constexpr T Underflow() {
return std::numeric_limits<T>::has_infinity
? std::numeric_limits<T>::infinity() * -1
: std::numeric_limits<T>::lowest();
}
};
template <typename Dst, template <typename> class S, typename Src>
constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
// For some reason clang generates much better code when the branch is
// structured exactly this way, rather than a sequence of checks.
return !constraint.IsOverflowFlagSet()
? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
: S<Dst>::Underflow())
// Skip this check for integral Src, which cannot be NaN.
: (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
? S<Dst>::Overflow()
: S<Dst>::NaN());
}
// We can reduce the number of conditions and get slightly better performance
// for normal signed and unsigned integer ranges. And in the specific case of
// Arm, we can use the optimized saturation instructions.
template <typename Dst, typename Src, typename Enable = void>
struct SaturateFastOp {
static const bool is_supported = false;
static constexpr Dst Do(Src value) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<Dst>();
}
};
template <typename Dst, typename Src>
struct SaturateFastOp<
Dst,
Src,
typename std::enable_if<std::is_integral<Src>::value &&
std::is_integral<Dst>::value &&
SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
static const bool is_supported = true;
static Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); }
};
template <typename Dst, typename Src>
struct SaturateFastOp<
Dst,
Src,
typename std::enable_if<std::is_integral<Src>::value &&
std::is_integral<Dst>::value &&
!SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
static const bool is_supported = true;
static Dst Do(Src value) {
// The exact order of the following is structured to hit the correct
// optimization heuristics across compilers. Do not change without
// checking the emitted code.
Dst saturated = CommonMaxOrMin<Dst, Src>(
IsMaxInRangeForNumericType<Dst, Src>() ||
(!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
? static_cast<Dst>(value)
: saturated;
}
};
// saturated_cast<> is analogous to static_cast<> for numeric types, except
// that the specified numeric conversion will saturate by default rather than
// overflow or underflow, and NaN assignment to an integral will return 0.
// All boundary condition behaviors can be overriden with a custom handler.
template <typename Dst,
template <typename> class SaturationHandler = SaturationDefaultLimits,
typename Src>
constexpr Dst saturated_cast(Src value) {
using SrcType = typename UnderlyingType<Src>::type;
return !IsCompileTimeConstant(value) &&
SaturateFastOp<Dst, SrcType>::is_supported &&
std::is_same<SaturationHandler<Dst>,
SaturationDefaultLimits<Dst>>::value
? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
: saturated_cast_impl<Dst, SaturationHandler, SrcType>(
static_cast<SrcType>(value),
DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
static_cast<SrcType>(value)));
}
// strict_cast<> is analogous to static_cast<> for numeric types, except that
// it will cause a compile failure if the destination type is not large enough
// to contain any value in the source type. It performs no runtime checking.
template <typename Dst, typename Src>
constexpr Dst strict_cast(Src value) {
using SrcType = typename UnderlyingType<Src>::type;
static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
// If you got here from a compiler error, it's because you tried to assign
// from a source type to a destination type that has insufficient range.
// The solution may be to change the destination type you're assigning to,
// and use one large enough to represent the source.
// Alternatively, you may be better served with the checked_cast<> or
// saturated_cast<> template functions for your particular use case.
static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
NUMERIC_RANGE_CONTAINED,
"The source type is out of range for the destination type. "
"Please see strict_cast<> comments for more information.");
return static_cast<Dst>(static_cast<SrcType>(value));
}
// Some wrappers to statically check that a type is in range.
template <typename Dst, typename Src, class Enable = void>
struct IsNumericRangeContained {
static const bool value = false;
};
template <typename Dst, typename Src>
struct IsNumericRangeContained<
Dst,
Src,
typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
ArithmeticOrUnderlyingEnum<Src>::value>::type> {
static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
NUMERIC_RANGE_CONTAINED;
};
// StrictNumeric implements compile time range checking between numeric types by
// wrapping assignment operations in a strict_cast. This class is intended to be
// used for function arguments and return types, to ensure the destination type
// can always contain the source type. This is essentially the same as enforcing
// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied
// incrementally at API boundaries, making it easier to convert code so that it
// compiles cleanly with truncation warnings enabled.
// This template should introduce no runtime overhead, but it also provides no
// runtime checking of any of the associated mathematical operations. Use
// CheckedNumeric for runtime range checks of the actual value being assigned.
template <typename T>
class StrictNumeric {
public:
using type = T;
constexpr StrictNumeric() : value_(0) {}
// Copy constructor.
template <typename Src>
constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
: value_(strict_cast<T>(rhs.value_)) {}
// This is not an explicit constructor because we implicitly upgrade regular
// numerics to StrictNumerics to make them easier to use.
template <typename Src>
constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
: value_(strict_cast<T>(value)) {}
// If you got here from a compiler error, it's because you tried to assign
// from a source type to a destination type that has insufficient range.
// The solution may be to change the destination type you're assigning to,
// and use one large enough to represent the source.
// If you're assigning from a CheckedNumeric<> class, you may be able to use
// the AssignIfValid() member function, specify a narrower destination type to
// the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
// of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
// If you've encountered an _ambiguous overload_ you can use a static_cast<>
// to explicitly cast the result to the destination type.
// If none of that works, you may be better served with the checked_cast<> or
// saturated_cast<> template functions for your particular use case.
template <typename Dst,
typename std::enable_if<
IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
constexpr operator Dst() const {
return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
}
private:
const T value_;
};
// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
template <typename T>
constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
const T value) {
return value;
}
#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
// Overload the ostream output operator to make logging work nicely.
template <typename T>
std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
os << static_cast<T>(value);
return os;
}
#endif
#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
template <typename L, typename R, \
typename std::enable_if< \
internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
constexpr bool operator OP(const L lhs, const R rhs) { \
return SafeCompare<NAME, typename UnderlyingType<L>::type, \
typename UnderlyingType<R>::type>(lhs, rhs); \
}
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)
} // namespace internal
using internal::as_signed;
using internal::as_unsigned;
using internal::checked_cast;
using internal::strict_cast;
using internal::saturated_cast;
using internal::SafeUnsignedAbs;
using internal::StrictNumeric;
using internal::MakeStrictNum;
using internal::IsValueInRangeForNumericType;
using internal::IsTypeInRangeForNumericType;
using internal::IsValueNegative;
// Explicitly make a shorter size_t alias for convenience.
using SizeT = StrictNumeric<size_t>;
} // namespace base
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_

View File

@@ -0,0 +1,51 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
#define BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
#include <cassert>
#include <limits>
#include <type_traits>
#include "base/numerics/safe_conversions_impl.h"
namespace base {
namespace internal {
// Fast saturation to a destination type.
template <typename Dst, typename Src>
struct SaturateFastAsmOp {
static constexpr bool is_supported =
std::is_signed<Src>::value && std::is_integral<Dst>::value &&
std::is_integral<Src>::value &&
IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&
IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&
!IsTypeInRangeForNumericType<Dst, Src>::value;
__attribute__((always_inline)) static Dst Do(Src value) {
int32_t src = value;
typename std::conditional<std::is_signed<Dst>::value, int32_t,
uint32_t>::type result;
if (std::is_signed<Dst>::value) {
asm("ssat %[dst], %[shift], %[src]"
: [dst] "=r"(result)
: [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32
? IntegerBitsPlusSign<Dst>::value
: 32));
} else {
asm("usat %[dst], %[shift], %[src]"
: [dst] "=r"(result)
: [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32
? IntegerBitsPlusSign<Dst>::value
: 31));
}
return static_cast<Dst>(result);
}
};
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_

View File

@@ -0,0 +1,850 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
#include <stdint.h>
#include <limits>
#include <type_traits>
#if defined(__GNUC__) || defined(__clang__)
#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define BASE_NUMERICS_LIKELY(x) (x)
#define BASE_NUMERICS_UNLIKELY(x) (x)
#endif
namespace base {
namespace internal {
// The std library doesn't provide a binary max_exponent for integers, however
// we can compute an analog using std::numeric_limits<>::digits.
template <typename NumericType>
struct MaxExponent {
static const int value = std::is_floating_point<NumericType>::value
? std::numeric_limits<NumericType>::max_exponent
: std::numeric_limits<NumericType>::digits + 1;
};
// The number of bits (including the sign) in an integer. Eliminates sizeof
// hacks.
template <typename NumericType>
struct IntegerBitsPlusSign {
static const int value = std::numeric_limits<NumericType>::digits +
std::is_signed<NumericType>::value;
};
// Helper templates for integer manipulations.
template <typename Integer>
struct PositionOfSignBit {
static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
};
// Determines if a numeric value is negative without throwing compiler
// warnings on: unsigned(value) < 0.
template <typename T,
typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
constexpr bool IsValueNegative(T value) {
static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
return value < 0;
}
template <typename T,
typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
constexpr bool IsValueNegative(T) {
static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
return false;
}
// This performs a fast negation, returning a signed value. It works on unsigned
// arguments, but probably doesn't do what you want for any unsigned value
// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
template <typename T>
constexpr typename std::make_signed<T>::type ConditionalNegate(
T x,
bool is_negative) {
static_assert(std::is_integral<T>::value, "Type must be integral");
using SignedT = typename std::make_signed<T>::type;
using UnsignedT = typename std::make_unsigned<T>::type;
return static_cast<SignedT>(
(static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
}
// This performs a safe, absolute value via unsigned overflow.
template <typename T>
constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
static_assert(std::is_integral<T>::value, "Type must be integral");
using UnsignedT = typename std::make_unsigned<T>::type;
return IsValueNegative(value) ? 0 - static_cast<UnsignedT>(value)
: static_cast<UnsignedT>(value);
}
// This allows us to switch paths on known compile-time constants.
#if defined(__clang__) || defined(__GNUC__)
constexpr bool CanDetectCompileTimeConstant() {
return true;
}
template <typename T>
constexpr bool IsCompileTimeConstant(const T v) {
return __builtin_constant_p(v);
}
#else
constexpr bool CanDetectCompileTimeConstant() {
return false;
}
template <typename T>
constexpr bool IsCompileTimeConstant(const T) {
return false;
}
#endif
template <typename T>
constexpr bool MustTreatAsConstexpr(const T v) {
// Either we can't detect a compile-time constant, and must always use the
// constexpr path, or we know we have a compile-time constant.
return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
}
// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
// Also used in a constexpr template to trigger a compilation failure on
// an error condition.
struct CheckOnFailure {
template <typename T>
static T HandleFailure() {
#if defined(_MSC_VER)
__debugbreak();
#elif defined(__GNUC__) || defined(__clang__)
__builtin_trap();
#else
((void)(*(volatile char*)0 = 0));
#endif
return T();
}
};
enum IntegerRepresentation {
INTEGER_REPRESENTATION_UNSIGNED,
INTEGER_REPRESENTATION_SIGNED
};
// A range for a given nunmeric Src type is contained for a given numeric Dst
// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
// We implement this as template specializations rather than simple static
// comparisons to ensure type correctness in our comparisons.
enum NumericRangeRepresentation {
NUMERIC_RANGE_NOT_CONTAINED,
NUMERIC_RANGE_CONTAINED
};
// Helper templates to statically determine if our destination type can contain
// maximum and minimum values represented by the source type.
template <typename Dst,
typename Src,
IntegerRepresentation DstSign = std::is_signed<Dst>::value
? INTEGER_REPRESENTATION_SIGNED
: INTEGER_REPRESENTATION_UNSIGNED,
IntegerRepresentation SrcSign = std::is_signed<Src>::value
? INTEGER_REPRESENTATION_SIGNED
: INTEGER_REPRESENTATION_UNSIGNED>
struct StaticDstRangeRelationToSrcRange;
// Same sign: Dst is guaranteed to contain Src only if its range is equal or
// larger.
template <typename Dst, typename Src, IntegerRepresentation Sign>
struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
static const NumericRangeRepresentation value =
MaxExponent<Dst>::value >= MaxExponent<Src>::value
? NUMERIC_RANGE_CONTAINED
: NUMERIC_RANGE_NOT_CONTAINED;
};
// Unsigned to signed: Dst is guaranteed to contain source only if its range is
// larger.
template <typename Dst, typename Src>
struct StaticDstRangeRelationToSrcRange<Dst,
Src,
INTEGER_REPRESENTATION_SIGNED,
INTEGER_REPRESENTATION_UNSIGNED> {
static const NumericRangeRepresentation value =
MaxExponent<Dst>::value > MaxExponent<Src>::value
? NUMERIC_RANGE_CONTAINED
: NUMERIC_RANGE_NOT_CONTAINED;
};
// Signed to unsigned: Dst cannot be statically determined to contain Src.
template <typename Dst, typename Src>
struct StaticDstRangeRelationToSrcRange<Dst,
Src,
INTEGER_REPRESENTATION_UNSIGNED,
INTEGER_REPRESENTATION_SIGNED> {
static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
};
// This class wraps the range constraints as separate booleans so the compiler
// can identify constants and eliminate unused code paths.
class RangeCheck {
public:
constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
: is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
constexpr bool operator==(const RangeCheck rhs) const {
return is_underflow_ == rhs.is_underflow_ &&
is_overflow_ == rhs.is_overflow_;
}
constexpr bool operator!=(const RangeCheck rhs) const {
return !(*this == rhs);
}
private:
// Do not change the order of these member variables. The integral conversion
// optimization depends on this exact order.
const bool is_underflow_;
const bool is_overflow_;
};
// The following helper template addresses a corner case in range checks for
// conversion from a floating-point type to an integral type of smaller range
// but larger precision (e.g. float -> unsigned). The problem is as follows:
// 1. Integral maximum is always one less than a power of two, so it must be
// truncated to fit the mantissa of the floating point. The direction of
// rounding is implementation defined, but by default it's always IEEE
// floats, which round to nearest and thus result in a value of larger
// magnitude than the integral value.
// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
// // is 4294967295u.
// 2. If the floating point value is equal to the promoted integral maximum
// value, a range check will erroneously pass.
// Example: (4294967296f <= 4294967295u) // This is true due to a precision
// // loss in rounding up to float.
// 3. When the floating point value is then converted to an integral, the
// resulting value is out of range for the target integral type and
// thus is implementation defined.
// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
// To fix this bug we manually truncate the maximum value when the destination
// type is an integral of larger precision than the source floating-point type,
// such that the resulting maximum is represented exactly as a floating point.
template <typename Dst, typename Src, template <typename> class Bounds>
struct NarrowingRange {
using SrcLimits = std::numeric_limits<Src>;
using DstLimits = typename std::numeric_limits<Dst>;
// Computes the mask required to make an accurate comparison between types.
static const int kShift =
(MaxExponent<Src>::value > MaxExponent<Dst>::value &&
SrcLimits::digits < DstLimits::digits)
? (DstLimits::digits - SrcLimits::digits)
: 0;
template <
typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
// Masks out the integer bits that are beyond the precision of the
// intermediate type used for comparison.
static constexpr T Adjust(T value) {
static_assert(std::is_same<T, Dst>::value, "");
static_assert(kShift < DstLimits::digits, "");
return static_cast<T>(
ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
IsValueNegative(value)));
}
template <typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* =
nullptr>
static constexpr T Adjust(T value) {
static_assert(std::is_same<T, Dst>::value, "");
static_assert(kShift == 0, "");
return value;
}
static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
};
template <typename Dst,
typename Src,
template <typename> class Bounds,
IntegerRepresentation DstSign = std::is_signed<Dst>::value
? INTEGER_REPRESENTATION_SIGNED
: INTEGER_REPRESENTATION_UNSIGNED,
IntegerRepresentation SrcSign = std::is_signed<Src>::value
? INTEGER_REPRESENTATION_SIGNED
: INTEGER_REPRESENTATION_UNSIGNED,
NumericRangeRepresentation DstRange =
StaticDstRangeRelationToSrcRange<Dst, Src>::value>
struct DstRangeRelationToSrcRangeImpl;
// The following templates are for ranges that must be verified at runtime. We
// split it into checks based on signedness to avoid confusing casts and
// compiler warnings on signed an unsigned comparisons.
// Same sign narrowing: The range is contained for normal limits.
template <typename Dst,
typename Src,
template <typename> class Bounds,
IntegerRepresentation DstSign,
IntegerRepresentation SrcSign>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
Bounds,
DstSign,
SrcSign,
NUMERIC_RANGE_CONTAINED> {
static constexpr RangeCheck Check(Src value) {
using SrcLimits = std::numeric_limits<Src>;
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
return RangeCheck(
static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
static_cast<Dst>(value) >= DstLimits::lowest(),
static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
static_cast<Dst>(value) <= DstLimits::max());
}
};
// Signed to signed narrowing: Both the upper and lower boundaries may be
// exceeded for standard limits.
template <typename Dst, typename Src, template <typename> class Bounds>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
Bounds,
INTEGER_REPRESENTATION_SIGNED,
INTEGER_REPRESENTATION_SIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
static constexpr RangeCheck Check(Src value) {
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
}
};
// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
// standard limits.
template <typename Dst, typename Src, template <typename> class Bounds>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
Bounds,
INTEGER_REPRESENTATION_UNSIGNED,
INTEGER_REPRESENTATION_UNSIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
static constexpr RangeCheck Check(Src value) {
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
return RangeCheck(
DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
value <= DstLimits::max());
}
};
// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
template <typename Dst, typename Src, template <typename> class Bounds>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
Bounds,
INTEGER_REPRESENTATION_SIGNED,
INTEGER_REPRESENTATION_UNSIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
static constexpr RangeCheck Check(Src value) {
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
using Promotion = decltype(Src() + Dst());
return RangeCheck(DstLimits::lowest() <= Dst(0) ||
static_cast<Promotion>(value) >=
static_cast<Promotion>(DstLimits::lowest()),
static_cast<Promotion>(value) <=
static_cast<Promotion>(DstLimits::max()));
}
};
// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
// and any negative value exceeds the lower boundary for standard limits.
template <typename Dst, typename Src, template <typename> class Bounds>
struct DstRangeRelationToSrcRangeImpl<Dst,
Src,
Bounds,
INTEGER_REPRESENTATION_UNSIGNED,
INTEGER_REPRESENTATION_SIGNED,
NUMERIC_RANGE_NOT_CONTAINED> {
static constexpr RangeCheck Check(Src value) {
using SrcLimits = std::numeric_limits<Src>;
using DstLimits = NarrowingRange<Dst, Src, Bounds>;
using Promotion = decltype(Src() + Dst());
return RangeCheck(
value >= Src(0) && (DstLimits::lowest() == 0 ||
static_cast<Dst>(value) >= DstLimits::lowest()),
static_cast<Promotion>(SrcLimits::max()) <=
static_cast<Promotion>(DstLimits::max()) ||
static_cast<Promotion>(value) <=
static_cast<Promotion>(DstLimits::max()));
}
};
// Simple wrapper for statically checking if a type's range is contained.
template <typename Dst, typename Src>
struct IsTypeInRangeForNumericType {
static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
NUMERIC_RANGE_CONTAINED;
};
template <typename Dst,
template <typename> class Bounds = std::numeric_limits,
typename Src>
constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
}
// Integer promotion templates used by the portable checked integer arithmetic.
template <size_t Size, bool IsSigned>
struct IntegerForDigitsAndSign;
#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
template <> \
struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
std::is_signed<I>::value> { \
using type = I; \
}
INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
#undef INTEGER_FOR_DIGITS_AND_SIGN
// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
// support 128-bit math, then the ArithmeticPromotion template below will need
// to be updated (or more likely replaced with a decltype expression).
static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
"Max integer size not supported for this toolchain.");
template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
struct TwiceWiderInteger {
using type =
typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
IsSigned>::type;
};
enum ArithmeticPromotionCategory {
LEFT_PROMOTION, // Use the type of the left-hand argument.
RIGHT_PROMOTION // Use the type of the right-hand argument.
};
// Determines the type that can represent the largest positive value.
template <typename Lhs,
typename Rhs,
ArithmeticPromotionCategory Promotion =
(MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
? LEFT_PROMOTION
: RIGHT_PROMOTION>
struct MaxExponentPromotion;
template <typename Lhs, typename Rhs>
struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
using type = Lhs;
};
template <typename Lhs, typename Rhs>
struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
using type = Rhs;
};
// Determines the type that can represent the lowest arithmetic value.
template <typename Lhs,
typename Rhs,
ArithmeticPromotionCategory Promotion =
std::is_signed<Lhs>::value
? (std::is_signed<Rhs>::value
? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
? LEFT_PROMOTION
: RIGHT_PROMOTION)
: LEFT_PROMOTION)
: (std::is_signed<Rhs>::value
? RIGHT_PROMOTION
: (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
? LEFT_PROMOTION
: RIGHT_PROMOTION))>
struct LowestValuePromotion;
template <typename Lhs, typename Rhs>
struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
using type = Lhs;
};
template <typename Lhs, typename Rhs>
struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
using type = Rhs;
};
// Determines the type that is best able to represent an arithmetic result.
template <
typename Lhs,
typename Rhs = Lhs,
bool is_intmax_type =
std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
value == IntegerBitsPlusSign<intmax_t>::value,
bool is_max_exponent =
StaticDstRangeRelationToSrcRange<
typename MaxExponentPromotion<Lhs, Rhs>::type,
Lhs>::value ==
NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
typename MaxExponentPromotion<Lhs, Rhs>::type,
Rhs>::value == NUMERIC_RANGE_CONTAINED>
struct BigEnoughPromotion;
// The side with the max exponent is big enough.
template <typename Lhs, typename Rhs, bool is_intmax_type>
struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
static const bool is_contained = true;
};
// We can use a twice wider type to fit.
template <typename Lhs, typename Rhs>
struct BigEnoughPromotion<Lhs, Rhs, false, false> {
using type =
typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
std::is_signed<Lhs>::value ||
std::is_signed<Rhs>::value>::type;
static const bool is_contained = true;
};
// No type is large enough.
template <typename Lhs, typename Rhs>
struct BigEnoughPromotion<Lhs, Rhs, true, false> {
using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
static const bool is_contained = false;
};
// We can statically check if operations on the provided types can wrap, so we
// can skip the checked operations if they're not needed. So, for an integer we
// care if the destination type preserves the sign and is twice the width of
// the source.
template <typename T, typename Lhs, typename Rhs = Lhs>
struct IsIntegerArithmeticSafe {
static const bool value =
!std::is_floating_point<T>::value &&
!std::is_floating_point<Lhs>::value &&
!std::is_floating_point<Rhs>::value &&
std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
};
// Promotes to a type that can represent any possible result of a binary
// arithmetic operation with the source types.
template <typename Lhs,
typename Rhs,
bool is_promotion_possible = IsIntegerArithmeticSafe<
typename std::conditional<std::is_signed<Lhs>::value ||
std::is_signed<Rhs>::value,
intmax_t,
uintmax_t>::type,
typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
struct FastIntegerArithmeticPromotion;
template <typename Lhs, typename Rhs>
struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
using type =
typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
std::is_signed<Lhs>::value ||
std::is_signed<Rhs>::value>::type;
static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
static const bool is_contained = true;
};
template <typename Lhs, typename Rhs>
struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
static const bool is_contained = false;
};
// Extracts the underlying type from an enum.
template <typename T, bool is_enum = std::is_enum<T>::value>
struct ArithmeticOrUnderlyingEnum;
template <typename T>
struct ArithmeticOrUnderlyingEnum<T, true> {
using type = typename std::underlying_type<T>::type;
static const bool value = std::is_arithmetic<type>::value;
};
template <typename T>
struct ArithmeticOrUnderlyingEnum<T, false> {
using type = T;
static const bool value = std::is_arithmetic<type>::value;
};
// The following are helper templates used in the CheckedNumeric class.
template <typename T>
class CheckedNumeric;
template <typename T>
class ClampedNumeric;
template <typename T>
class StrictNumeric;
// Used to treat CheckedNumeric and arithmetic underlying types the same.
template <typename T>
struct UnderlyingType {
using type = typename ArithmeticOrUnderlyingEnum<T>::type;
static const bool is_numeric = std::is_arithmetic<type>::value;
static const bool is_checked = false;
static const bool is_clamped = false;
static const bool is_strict = false;
};
template <typename T>
struct UnderlyingType<CheckedNumeric<T>> {
using type = T;
static const bool is_numeric = true;
static const bool is_checked = true;
static const bool is_clamped = false;
static const bool is_strict = false;
};
template <typename T>
struct UnderlyingType<ClampedNumeric<T>> {
using type = T;
static const bool is_numeric = true;
static const bool is_checked = false;
static const bool is_clamped = true;
static const bool is_strict = false;
};
template <typename T>
struct UnderlyingType<StrictNumeric<T>> {
using type = T;
static const bool is_numeric = true;
static const bool is_checked = false;
static const bool is_clamped = false;
static const bool is_strict = true;
};
template <typename L, typename R>
struct IsCheckedOp {
static const bool value =
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
};
template <typename L, typename R>
struct IsClampedOp {
static const bool value =
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
!(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
};
template <typename L, typename R>
struct IsStrictOp {
static const bool value =
UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
(UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
!(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
!(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
};
// as_signed<> returns the supplied integral value (or integral castable
// Numeric template) cast as a signed integral of equivalent precision.
// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
template <typename Src>
constexpr typename std::make_signed<
typename base::internal::UnderlyingType<Src>::type>::type
as_signed(const Src value) {
static_assert(std::is_integral<decltype(as_signed(value))>::value,
"Argument must be a signed or unsigned integer type.");
return static_cast<decltype(as_signed(value))>(value);
}
// as_unsigned<> returns the supplied integral value (or integral castable
// Numeric template) cast as an unsigned integral of equivalent precision.
// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
template <typename Src>
constexpr typename std::make_unsigned<
typename base::internal::UnderlyingType<Src>::type>::type
as_unsigned(const Src value) {
static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
"Argument must be a signed or unsigned integer type.");
return static_cast<decltype(as_unsigned(value))>(value);
}
template <typename L, typename R>
constexpr bool IsLessImpl(const L lhs,
const R rhs,
const RangeCheck l_range,
const RangeCheck r_range) {
return l_range.IsUnderflow() || r_range.IsOverflow() ||
(l_range == r_range &&
static_cast<decltype(lhs + rhs)>(lhs) <
static_cast<decltype(lhs + rhs)>(rhs));
}
template <typename L, typename R>
struct IsLess {
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
"Types must be numeric.");
static constexpr bool Test(const L lhs, const R rhs) {
return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
DstRangeRelationToSrcRange<L>(rhs));
}
};
template <typename L, typename R>
constexpr bool IsLessOrEqualImpl(const L lhs,
const R rhs,
const RangeCheck l_range,
const RangeCheck r_range) {
return l_range.IsUnderflow() || r_range.IsOverflow() ||
(l_range == r_range &&
static_cast<decltype(lhs + rhs)>(lhs) <=
static_cast<decltype(lhs + rhs)>(rhs));
}
template <typename L, typename R>
struct IsLessOrEqual {
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
"Types must be numeric.");
static constexpr bool Test(const L lhs, const R rhs) {
return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
DstRangeRelationToSrcRange<L>(rhs));
}
};
template <typename L, typename R>
constexpr bool IsGreaterImpl(const L lhs,
const R rhs,
const RangeCheck l_range,
const RangeCheck r_range) {
return l_range.IsOverflow() || r_range.IsUnderflow() ||
(l_range == r_range &&
static_cast<decltype(lhs + rhs)>(lhs) >
static_cast<decltype(lhs + rhs)>(rhs));
}
template <typename L, typename R>
struct IsGreater {
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
"Types must be numeric.");
static constexpr bool Test(const L lhs, const R rhs) {
return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
DstRangeRelationToSrcRange<L>(rhs));
}
};
template <typename L, typename R>
constexpr bool IsGreaterOrEqualImpl(const L lhs,
const R rhs,
const RangeCheck l_range,
const RangeCheck r_range) {
return l_range.IsOverflow() || r_range.IsUnderflow() ||
(l_range == r_range &&
static_cast<decltype(lhs + rhs)>(lhs) >=
static_cast<decltype(lhs + rhs)>(rhs));
}
template <typename L, typename R>
struct IsGreaterOrEqual {
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
"Types must be numeric.");
static constexpr bool Test(const L lhs, const R rhs) {
return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
DstRangeRelationToSrcRange<L>(rhs));
}
};
template <typename L, typename R>
struct IsEqual {
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
"Types must be numeric.");
static constexpr bool Test(const L lhs, const R rhs) {
return DstRangeRelationToSrcRange<R>(lhs) ==
DstRangeRelationToSrcRange<L>(rhs) &&
static_cast<decltype(lhs + rhs)>(lhs) ==
static_cast<decltype(lhs + rhs)>(rhs);
}
};
template <typename L, typename R>
struct IsNotEqual {
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
"Types must be numeric.");
static constexpr bool Test(const L lhs, const R rhs) {
return DstRangeRelationToSrcRange<R>(lhs) !=
DstRangeRelationToSrcRange<L>(rhs) ||
static_cast<decltype(lhs + rhs)>(lhs) !=
static_cast<decltype(lhs + rhs)>(rhs);
}
};
// These perform the actual math operations on the CheckedNumerics.
// Binary arithmetic operations.
template <template <typename, typename> class C, typename L, typename R>
constexpr bool SafeCompare(const L lhs, const R rhs) {
static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
"Types must be numeric.");
using Promotion = BigEnoughPromotion<L, R>;
using BigType = typename Promotion::type;
return Promotion::is_contained
// Force to a larger type for speed if both are contained.
? C<BigType, BigType>::Test(
static_cast<BigType>(static_cast<L>(lhs)),
static_cast<BigType>(static_cast<R>(rhs)))
// Let the template functions figure it out for mixed types.
: C<L, R>::Test(lhs, rhs);
}
template <typename Dst, typename Src>
constexpr bool IsMaxInRangeForNumericType() {
return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
std::numeric_limits<Src>::max());
}
template <typename Dst, typename Src>
constexpr bool IsMinInRangeForNumericType() {
return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
std::numeric_limits<Src>::lowest());
}
template <typename Dst, typename Src>
constexpr Dst CommonMax() {
return !IsMaxInRangeForNumericType<Dst, Src>()
? Dst(std::numeric_limits<Dst>::max())
: Dst(std::numeric_limits<Src>::max());
}
template <typename Dst, typename Src>
constexpr Dst CommonMin() {
return !IsMinInRangeForNumericType<Dst, Src>()
? Dst(std::numeric_limits<Dst>::lowest())
: Dst(std::numeric_limits<Src>::lowest());
}
// This is a wrapper to generate return the max or min for a supplied type.
// If the argument is false, the returned value is the maximum. If true the
// returned value is the minimum.
template <typename Dst, typename Src = Dst>
constexpr Dst CommonMaxOrMin(bool is_min) {
return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
}
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_

View File

@@ -0,0 +1,12 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_MATH_H_
#define BASE_NUMERICS_SAFE_MATH_H_
#include "base/numerics/checked_math.h"
#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_conversions.h"
#endif // BASE_NUMERICS_SAFE_MATH_H_

View File

@@ -0,0 +1,122 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
#define BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
#include <cassert>
#include <limits>
#include <type_traits>
#include "base/numerics/safe_conversions.h"
namespace base {
namespace internal {
template <typename T, typename U>
struct CheckedMulFastAsmOp {
static const bool is_supported =
FastIntegerArithmeticPromotion<T, U>::is_contained;
// The following is much more efficient than the Clang and GCC builtins for
// performing overflow-checked multiplication when a twice wider type is
// available. The below compiles down to 2-3 instructions, depending on the
// width of the types in use.
// As an example, an int32_t multiply compiles to:
// smull r0, r1, r0, r1
// cmp r1, r1, asr #31
// And an int16_t multiply compiles to:
// smulbb r1, r1, r0
// asr r2, r1, #16
// cmp r2, r1, asr #15
template <typename V>
__attribute__((always_inline)) static bool Do(T x, U y, V* result) {
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
Promotion presult;
presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
*result = static_cast<V>(presult);
return IsValueInRangeForNumericType<V>(presult);
}
};
template <typename T, typename U>
struct ClampedAddFastAsmOp {
static const bool is_supported =
BigEnoughPromotion<T, U>::is_contained &&
IsTypeInRangeForNumericType<
int32_t,
typename BigEnoughPromotion<T, U>::type>::value;
template <typename V>
__attribute__((always_inline)) static V Do(T x, U y) {
// This will get promoted to an int, so let the compiler do whatever is
// clever and rely on the saturated cast to bounds check.
if (IsIntegerArithmeticSafe<int, T, U>::value)
return saturated_cast<V>(x + y);
int32_t result;
int32_t x_i32 = checked_cast<int32_t>(x);
int32_t y_i32 = checked_cast<int32_t>(y);
asm("qadd %[result], %[first], %[second]"
: [result] "=r"(result)
: [first] "r"(x_i32), [second] "r"(y_i32));
return saturated_cast<V>(result);
}
};
template <typename T, typename U>
struct ClampedSubFastAsmOp {
static const bool is_supported =
BigEnoughPromotion<T, U>::is_contained &&
IsTypeInRangeForNumericType<
int32_t,
typename BigEnoughPromotion<T, U>::type>::value;
template <typename V>
__attribute__((always_inline)) static V Do(T x, U y) {
// This will get promoted to an int, so let the compiler do whatever is
// clever and rely on the saturated cast to bounds check.
if (IsIntegerArithmeticSafe<int, T, U>::value)
return saturated_cast<V>(x - y);
int32_t result;
int32_t x_i32 = checked_cast<int32_t>(x);
int32_t y_i32 = checked_cast<int32_t>(y);
asm("qsub %[result], %[first], %[second]"
: [result] "=r"(result)
: [first] "r"(x_i32), [second] "r"(y_i32));
return saturated_cast<V>(result);
}
};
template <typename T, typename U>
struct ClampedMulFastAsmOp {
static const bool is_supported = CheckedMulFastAsmOp<T, U>::is_supported;
template <typename V>
__attribute__((always_inline)) static V Do(T x, U y) {
// Use the CheckedMulFastAsmOp for full-width 32-bit values, because
// it's fewer instructions than promoting and then saturating.
if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&
!IsIntegerArithmeticSafe<uint32_t, T, U>::value) {
V result;
if (CheckedMulFastAsmOp<T, U>::Do(x, y, &result))
return result;
return CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
}
assert((FastIntegerArithmeticPromotion<T, U>::is_contained));
using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
return saturated_cast<V>(static_cast<Promotion>(x) *
static_cast<Promotion>(y));
}
};
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_

View File

@@ -0,0 +1,157 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
#define BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
#include <cassert>
#include <limits>
#include <type_traits>
#include "base/numerics/safe_conversions.h"
#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
#include "base/numerics/safe_math_arm_impl.h"
#define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
#else
#define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
#endif
namespace base {
namespace internal {
// These are the non-functioning boilerplate implementations of the optimized
// safe math routines.
#if !BASE_HAS_ASSEMBLER_SAFE_MATH
template <typename T, typename U>
struct CheckedMulFastAsmOp {
static const bool is_supported = false;
template <typename V>
static constexpr bool Do(T, U, V*) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<bool>();
}
};
template <typename T, typename U>
struct ClampedAddFastAsmOp {
static const bool is_supported = false;
template <typename V>
static constexpr V Do(T, U) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<V>();
}
};
template <typename T, typename U>
struct ClampedSubFastAsmOp {
static const bool is_supported = false;
template <typename V>
static constexpr V Do(T, U) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<V>();
}
};
template <typename T, typename U>
struct ClampedMulFastAsmOp {
static const bool is_supported = false;
template <typename V>
static constexpr V Do(T, U) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<V>();
}
};
#endif // BASE_HAS_ASSEMBLER_SAFE_MATH
#undef BASE_HAS_ASSEMBLER_SAFE_MATH
template <typename T, typename U>
struct CheckedAddFastOp {
static const bool is_supported = true;
template <typename V>
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
return !__builtin_add_overflow(x, y, result);
}
};
template <typename T, typename U>
struct CheckedSubFastOp {
static const bool is_supported = true;
template <typename V>
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
return !__builtin_sub_overflow(x, y, result);
}
};
template <typename T, typename U>
struct CheckedMulFastOp {
#if defined(__clang__)
// TODO(jschuh): Get the Clang runtime library issues sorted out so we can
// support full-width, mixed-sign multiply builtins.
// https://crbug.com/613003
// We can support intptr_t, uintptr_t, or a smaller common type.
static const bool is_supported =
(IsTypeInRangeForNumericType<intptr_t, T>::value &&
IsTypeInRangeForNumericType<intptr_t, U>::value) ||
(IsTypeInRangeForNumericType<uintptr_t, T>::value &&
IsTypeInRangeForNumericType<uintptr_t, U>::value);
#else
static const bool is_supported = true;
#endif
template <typename V>
__attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
return CheckedMulFastAsmOp<T, U>::is_supported
? CheckedMulFastAsmOp<T, U>::Do(x, y, result)
: !__builtin_mul_overflow(x, y, result);
}
};
template <typename T, typename U>
struct ClampedAddFastOp {
static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;
template <typename V>
__attribute__((always_inline)) static V Do(T x, U y) {
return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);
}
};
template <typename T, typename U>
struct ClampedSubFastOp {
static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;
template <typename V>
__attribute__((always_inline)) static V Do(T x, U y) {
return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);
}
};
template <typename T, typename U>
struct ClampedMulFastOp {
static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;
template <typename V>
__attribute__((always_inline)) static V Do(T x, U y) {
return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);
}
};
template <typename T>
struct ClampedNegFastOp {
static const bool is_supported = std::is_signed<T>::value;
__attribute__((always_inline)) static T Do(T value) {
// Use this when there is no assembler path available.
if (!ClampedSubFastAsmOp<T, T>::is_supported) {
T result;
return !__builtin_sub_overflow(T(0), value, &result)
? result
: std::numeric_limits<T>::max();
}
// Fallback to the normal subtraction path.
return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
}
};
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_

View File

@@ -0,0 +1,240 @@
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
#include <stddef.h>
#include <stdint.h>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <type_traits>
#include "base/numerics/safe_conversions.h"
#ifdef __asmjs__
// Optimized safe math instructions are incompatible with asmjs.
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
// Where available use builtin math overflow support on Clang and GCC.
#elif !defined(__native_client__) && \
((defined(__clang__) && \
((__clang_major__ > 3) || \
(__clang_major__ == 3 && __clang_minor__ >= 4))) || \
(defined(__GNUC__) && __GNUC__ >= 5))
#include "base/numerics/safe_math_clang_gcc_impl.h"
#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
#else
#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
#endif
namespace base {
namespace internal {
// These are the non-functioning boilerplate implementations of the optimized
// safe math routines.
#if !BASE_HAS_OPTIMIZED_SAFE_MATH
template <typename T, typename U>
struct CheckedAddFastOp {
static const bool is_supported = false;
template <typename V>
static constexpr bool Do(T, U, V*) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<bool>();
}
};
template <typename T, typename U>
struct CheckedSubFastOp {
static const bool is_supported = false;
template <typename V>
static constexpr bool Do(T, U, V*) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<bool>();
}
};
template <typename T, typename U>
struct CheckedMulFastOp {
static const bool is_supported = false;
template <typename V>
static constexpr bool Do(T, U, V*) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<bool>();
}
};
template <typename T, typename U>
struct ClampedAddFastOp {
static const bool is_supported = false;
template <typename V>
static constexpr V Do(T, U) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<V>();
}
};
template <typename T, typename U>
struct ClampedSubFastOp {
static const bool is_supported = false;
template <typename V>
static constexpr V Do(T, U) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<V>();
}
};
template <typename T, typename U>
struct ClampedMulFastOp {
static const bool is_supported = false;
template <typename V>
static constexpr V Do(T, U) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<V>();
}
};
template <typename T>
struct ClampedNegFastOp {
static const bool is_supported = false;
static constexpr T Do(T) {
// Force a compile failure if instantiated.
return CheckOnFailure::template HandleFailure<T>();
}
};
#endif // BASE_HAS_OPTIMIZED_SAFE_MATH
#undef BASE_HAS_OPTIMIZED_SAFE_MATH
// This is used for UnsignedAbs, where we need to support floating-point
// template instantiations even though we don't actually support the operations.
// However, there is no corresponding implementation of e.g. SafeUnsignedAbs,
// so the float versions will not compile.
template <typename Numeric,
bool IsInteger = std::is_integral<Numeric>::value,
bool IsFloat = std::is_floating_point<Numeric>::value>
struct UnsignedOrFloatForSize;
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, true, false> {
using type = typename std::make_unsigned<Numeric>::type;
};
template <typename Numeric>
struct UnsignedOrFloatForSize<Numeric, false, true> {
using type = Numeric;
};
// Wrap the unary operations to allow SFINAE when instantiating integrals versus
// floating points. These don't perform any overflow checking. Rather, they
// exhibit well-defined overflow semantics and rely on the caller to detect
// if an overflow occured.
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr T NegateWrapper(T value) {
using UnsignedT = typename std::make_unsigned<T>::type;
// This will compile to a NEG on Intel, and is normal negation on ARM.
return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T NegateWrapper(T value) {
return -value;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
return ~value;
}
template <typename T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
constexpr T AbsWrapper(T value) {
return static_cast<T>(SafeUnsignedAbs(value));
}
template <
typename T,
typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
constexpr T AbsWrapper(T value) {
return value < 0 ? -value : value;
}
template <template <typename, typename, typename> class M,
typename L,
typename R>
struct MathWrapper {
using math = M<typename UnderlyingType<L>::type,
typename UnderlyingType<R>::type,
void>;
using type = typename math::result_type;
};
// These variadic templates work out the return types.
// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
template <template <typename, typename, typename> class M,
typename L,
typename R,
typename... Args>
struct ResultType;
template <template <typename, typename, typename> class M,
typename L,
typename R>
struct ResultType<M, L, R> {
using type = typename MathWrapper<M, L, R>::type;
};
template <template <typename, typename, typename> class M,
typename L,
typename R,
typename... Args>
struct ResultType {
using type =
typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
};
// The following macros are just boilerplate for the standard arithmetic
// operator overloads and variadic function templates. A macro isn't the nicest
// solution, but it beats rewriting these over and over again.
#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \
template <typename L, typename R, typename... Args> \
constexpr CLASS##Numeric< \
typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
args...); \
}
#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
/* Binary arithmetic operator for all CLASS##Numeric operations. */ \
template <typename L, typename R, \
typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \
nullptr> \
constexpr CLASS##Numeric< \
typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
operator OP(const L lhs, const R rhs) { \
return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
rhs); \
} \
/* Assignment arithmetic operator implementation from CLASS##Numeric. */ \
template <typename L> \
template <typename R> \
constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \
const R rhs) { \
return MathOp<CLASS##OP_NAME##Op>(rhs); \
} \
/* Variadic arithmetic functions that return CLASS##Numeric. */ \
BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
} // namespace internal
} // namespace base
#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_

View File

@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/component-detection-manifest.json",
"Registrations": [
{
"component": {
"type": "git",
"git": {
"repositoryUrl": "https://github.com/chromium/chromium",
"commitHash": "d8710dd959da8e3be56f20af8cc94fbf560fbb6b"
}
}
}
],
"Version": 1
}

View File

@@ -135,17 +135,5 @@
</Target>
<!-- **END VC LIBS HACK** -->
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>

View File

@@ -20,8 +20,9 @@
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
@@ -156,14 +157,6 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!--
By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf.

View File

@@ -13,8 +13,9 @@
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<!-- ========================= XAML files ======================== -->
@@ -90,14 +91,6 @@
</Link>
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
</packages>

View File

@@ -17,10 +17,9 @@
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalThemeHelpers>true</TerminalThemeHelpers>
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
@@ -142,14 +141,6 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!-- Override GetPackagingOutputs to roll up all our dependencies.
This ensures that when the WAP packaging project asks what files go into
the package, we tell it.
@@ -225,4 +216,3 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
</Project>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
</packages>

View File

@@ -7,11 +7,27 @@
#include "Row.hpp"
#include "textBuffer.hpp"
static std::atomic<uint64_t> s_revision{ 0 };
ImageSlice::ImageSlice(const til::size cellSize) noexcept :
_cellSize{ cellSize }
{
}
void ImageSlice::BumpRevision() noexcept
{
// Avoid setting the revision to 0. This allows the renderer to use 0 as a sentinel value.
do
{
_revision = s_revision.fetch_add(1, std::memory_order_relaxed);
} while (_revision == 0);
}
uint64_t ImageSlice::Revision() const noexcept
{
return _revision;
}
til::size ImageSlice::CellSize() const noexcept
{
return _cellSize;
@@ -49,7 +65,6 @@ RGBQUAD* ImageSlice::MutablePixels(const til::CoordType columnBegin, const til::
_columnBegin = existingData ? std::min(_columnBegin, columnBegin) : columnBegin;
_columnEnd = existingData ? std::max(_columnEnd, columnEnd) : columnEnd;
_pixelWidth = (_columnEnd - _columnBegin) * _cellSize.width;
_pixelWidth = (_pixelWidth + 3) & ~3; // Renderer needs this as a multiple of 4
const auto bufferSize = _pixelWidth * _cellSize.height;
if (existingData)
{
@@ -108,9 +123,8 @@ void ImageSlice::CopyBlock(const TextBuffer& srcBuffer, const til::rect srcRect,
void ImageSlice::CopyRow(const ROW& srcRow, ROW& dstRow)
{
const auto& srcSlice = srcRow.GetImageSlice();
auto& dstSlice = dstRow.GetMutableImageSlice();
dstSlice = srcSlice ? std::make_unique<ImageSlice>(*srcSlice) : nullptr;
const auto srcSlice = srcRow.GetImageSlice();
dstRow.SetImageSlice(srcSlice ? std::make_unique<ImageSlice>(*srcSlice) : nullptr);
}
void ImageSlice::CopyCells(const ROW& srcRow, const til::CoordType srcColumn, ROW& dstRow, const til::CoordType dstColumnBegin, const til::CoordType dstColumnEnd)
@@ -119,24 +133,25 @@ void ImageSlice::CopyCells(const ROW& srcRow, const til::CoordType srcColumn, RO
// a blank image into the destination, which is the same thing as an erase.
// Also if the line renditions are different, there's no meaningful way to
// copy the image content, so we also just treat that as an erase.
const auto& srcSlice = srcRow.GetImageSlice();
const auto srcSlice = srcRow.GetImageSlice();
if (!srcSlice || srcRow.GetLineRendition() != dstRow.GetLineRendition()) [[likely]]
{
ImageSlice::EraseCells(dstRow, dstColumnBegin, dstColumnEnd);
}
else
{
auto& dstSlice = dstRow.GetMutableImageSlice();
auto dstSlice = dstRow.GetMutableImageSlice();
if (!dstSlice)
{
dstSlice = std::make_unique<ImageSlice>(srcSlice->CellSize());
dstSlice = dstRow.SetImageSlice(std::make_unique<ImageSlice>(srcSlice->CellSize()));
__assume(dstSlice != nullptr);
}
const auto scale = srcRow.GetLineRendition() != LineRendition::SingleWidth ? 1 : 0;
if (dstSlice->_copyCells(*srcSlice, srcColumn << scale, dstColumnBegin << scale, dstColumnEnd << scale))
{
// If _copyCells returns true, that means the destination was
// completely erased, so we can delete this slice.
dstSlice = nullptr;
dstRow.SetImageSlice(nullptr);
}
}
}
@@ -203,7 +218,7 @@ void ImageSlice::EraseCells(TextBuffer& buffer, const til::point at, const size_
void ImageSlice::EraseCells(ROW& row, const til::CoordType columnBegin, const til::CoordType columnEnd)
{
auto& imageSlice = row.GetMutableImageSlice();
const auto imageSlice = row.GetMutableImageSlice();
if (imageSlice) [[unlikely]]
{
const auto scale = row.GetLineRendition() != LineRendition::SingleWidth ? 1 : 0;
@@ -211,7 +226,7 @@ void ImageSlice::EraseCells(ROW& row, const til::CoordType columnBegin, const ti
{
// If _eraseCells returns true, that means the image was
// completely erased, so we can delete this slice.
imageSlice = nullptr;
row.SetImageSlice(nullptr);
}
}
}

View File

@@ -26,6 +26,9 @@ public:
ImageSlice(const ImageSlice& rhs) = default;
ImageSlice(const til::size cellSize) noexcept;
void BumpRevision() noexcept;
uint64_t Revision() const noexcept;
til::size CellSize() const noexcept;
til::CoordType ColumnOffset() const noexcept;
til::CoordType PixelWidth() const noexcept;
@@ -45,6 +48,7 @@ private:
bool _copyCells(const ImageSlice& srcSlice, const til::CoordType srcColumn, const til::CoordType dstColumnBegin, const til::CoordType dstColumnEnd);
bool _eraseCells(const til::CoordType columnBegin, const til::CoordType columnEnd);
uint64_t _revision = 0;
til::size _cellSize;
std::vector<RGBQUAD> _pixelBuffer;
til::CoordType _columnBegin = 0;

View File

@@ -9,6 +9,10 @@
#include "../../types/inc/convert.hpp"
#include "../../inc/conattrs.hpp"
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
// initializer list leads to this warning. Can probably be removed in the future.
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR };
OutputCell::OutputCell() noexcept :

View File

@@ -11,6 +11,10 @@
#include "../../types/inc/GlyphWidth.hpp"
#include "../../inc/conattrs.hpp"
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
// initializer list leads to this warning. Can probably be removed in the future.
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR, INVALID_COLOR };
// Routine Description:
@@ -274,7 +278,7 @@ OutputCellIterator& OutputCellIterator::operator++()
}
case Mode::CharInfo:
{
// Walk forward by one because charinfos are just the legacy version of cells and prealigned to columns
// Walk forward by one because char infos are just the legacy version of cells and prealigned to columns
_pos++;
if (operator bool())
{

View File

@@ -5,6 +5,10 @@
#include "OutputCellView.hpp"
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
// initializer list leads to this warning. Can probably be removed in the future.
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
// Routine Description:
// - Constructs a read-only view of data formatted as a single output buffer cell
// Arguments:

View File

@@ -162,7 +162,7 @@ til::CoordType CharToColumnMapper::GetTrailingColumnAt(const wchar_t* str) noexc
ROW::ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute) :
_charsBuffer{ charsBuffer },
_chars{ charsBuffer, rowWidth },
_charOffsets{ charOffsetsBuffer, gsl::narrow_cast<size_t>(rowWidth) + 1u },
_charOffsets{ charOffsetsBuffer, ::base::strict_cast<size_t>(rowWidth) + 1u },
_attr{ rowWidth, fillAttribute },
_columnCount{ rowWidth }
{
@@ -965,14 +965,26 @@ std::vector<uint16_t> ROW::GetHyperlinks() const
return ids;
}
const ImageSlice::Pointer& ROW::GetImageSlice() const noexcept
ImageSlice* ROW::SetImageSlice(ImageSlice::Pointer imageSlice) noexcept
{
return _imageSlice;
_imageSlice = std::move(imageSlice);
return GetMutableImageSlice();
}
ImageSlice::Pointer& ROW::GetMutableImageSlice() noexcept
const ImageSlice* ROW::GetImageSlice() const noexcept
{
return _imageSlice;
return _imageSlice.get();
}
ImageSlice* ROW::GetMutableImageSlice() noexcept
{
const auto ptr = _imageSlice.get();
if (!ptr)
{
return nullptr;
}
ptr->BumpRevision();
return ptr;
}
uint16_t ROW::size() const noexcept
@@ -1088,7 +1100,7 @@ DbcsAttribute ROW::DbcsAttrAt(til::CoordType column) const noexcept
attr = DbcsAttribute::Trailing;
}
// Safety: col+1 is [1, _columnCount].
else if (_uncheckedIsTrailer(gsl::narrow_cast<size_t>(col) + 1u))
else if (_uncheckedIsTrailer(::base::strict_cast<size_t>(col) + 1u))
{
attr = DbcsAttribute::Leading;
}

View File

@@ -152,8 +152,9 @@ public:
const til::small_rle<TextAttribute, uint16_t, 1>& Attributes() const noexcept;
TextAttribute GetAttrByColumn(til::CoordType column) const;
std::vector<uint16_t> GetHyperlinks() const;
const ImageSlice::Pointer& GetImageSlice() const noexcept;
ImageSlice::Pointer& GetMutableImageSlice() noexcept;
ImageSlice* SetImageSlice(ImageSlice::Pointer imageSlice) noexcept;
const ImageSlice* GetImageSlice() const noexcept;
ImageSlice* GetMutableImageSlice() noexcept;
uint16_t size() const noexcept;
til::CoordType GetLastNonSpaceColumn() const noexcept;
til::CoordType MeasureLeft() const noexcept;
@@ -299,8 +300,6 @@ private:
til::small_rle<TextAttribute, uint16_t, 1> _attr;
// The width of the row in visual columns.
uint16_t _columnCount = 0;
// Stores any image content covering the row.
ImageSlice::Pointer _imageSlice;
// Stores double-width/height (DECSWL/DECDWL/DECDHL) attributes.
LineRendition _lineRendition = LineRendition::SingleWidth;
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line
@@ -309,6 +308,9 @@ private:
bool _doubleBytePadded = false;
std::optional<ScrollbarData> _promptData = std::nullopt;
// Stores any image content covering the row.
ImageSlice::Pointer _imageSlice;
};
#ifdef UNIT_TESTING

View File

@@ -65,47 +65,6 @@ void TextAttribute::SetLegacyDefaultAttributes(const WORD defaultAttributes) noe
gsl::at(s_legacyBackgroundColorMap, s_legacyDefaultBackground) = TextColor{};
}
// Routine Description:
// Pursuant to GH#6807
// This routine replaces VT colors from the 16-color set with the "default"
// flag. It is intended to be used as part of the "VT Quirk" in
// WriteConsole[AW].
//
// There is going to be a very long tail of applications that will
// explicitly request VT SGR 40/37 when what they really want is to
// SetConsoleTextAttribute() with a black background/white foreground.
// Instead of making those applications look bad (and therefore making us
// look bad, because we're releasing this as an update to something that
// "looks good" already), we're introducing this compatibility hack. Before
// the color reckoning in GH#6698 + GH#6506, *every* color was subject to
// being spontaneously and erroneously turned into the default color. Now,
// only the 16-color palette value that matches the active console
// background color will be destroyed when the quirk is enabled.
//
// This is not intended to be a long-term solution. This comment will be
// discovered in forty years(*) time and people will laugh at our hubris.
//
// *it doesn't matter when you're reading this, it will always be 40 years
// from now.
TextAttribute TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept
{
const auto fg{ attribute.GetForeground() };
const auto bg{ attribute.GetBackground() };
auto copy{ attribute };
if (fg.IsIndex16() &&
attribute.IsIntense() == WI_IsFlagSet(s_ansiDefaultForeground, FOREGROUND_INTENSITY) &&
fg.GetIndex() == (s_ansiDefaultForeground & ~FOREGROUND_INTENSITY))
{
// We don't want to turn 1;37m into 39m (or even 1;39m), as this was meant to mimic a legacy color.
copy.SetDefaultForeground();
}
if (bg.IsIndex16() && bg.GetIndex() == s_ansiDefaultBackground)
{
copy.SetDefaultBackground();
}
return copy;
}
// Routine Description:
// - Returns a WORD with legacy-style attributes for this textattribute.
// Parameters:

View File

@@ -93,7 +93,6 @@ public:
}
static void SetLegacyDefaultAttributes(const WORD defaultAttributes) noexcept;
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
WORD GetLegacyAttributes() const noexcept;
bool IsTopHorizontalDisplayed() const noexcept;

View File

@@ -82,7 +82,8 @@ public:
static constexpr size_t FRAME_FOREGROUND = 263;
static constexpr size_t FRAME_BACKGROUND = 264;
static constexpr size_t CURSOR_COLOR = 265;
static constexpr size_t TABLE_SIZE = 266;
static constexpr size_t SELECTION_BACKGROUND = 266;
static constexpr size_t TABLE_SIZE = 267;
constexpr TextColor() noexcept :
_meta{ ColorType::IsDefault },

View File

@@ -16,7 +16,7 @@ bool Search::IsStale(const Microsoft::Console::Render::IRenderData& renderData,
_lastMutationId != renderData.GetTextBuffer().GetLastMutationId();
}
bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse)
void Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse)
{
const auto& textBuffer = renderData.GetTextBuffer();
@@ -30,15 +30,15 @@ bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const st
_results = std::move(result).value_or(std::vector<til::point_span>{});
_index = reverse ? gsl::narrow_cast<ptrdiff_t>(_results.size()) - 1 : 0;
_step = reverse ? -1 : 1;
return true;
}
void Search::MoveToCurrentSelection()
{
if (_renderData->IsSelectionActive())
{
MoveToPoint(_renderData->GetTextBuffer().ScreenToBufferPosition(_renderData->GetSelectionAnchor()));
}
else if (const auto span = _renderData->GetSearchHighlightFocused())
{
MoveToPoint(_step > 0 ? span->start : span->end);
}
}
void Search::MoveToPoint(const til::point anchor) noexcept

View File

@@ -36,9 +36,8 @@ public:
Search() = default;
bool IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags) const noexcept;
bool Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse);
void Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse);
void MoveToCurrentSelection();
void MoveToPoint(til::point anchor) noexcept;
void MovePastPoint(til::point anchor) noexcept;
void FindNext(bool reverse) noexcept;

View File

@@ -12,6 +12,10 @@
#include "../types/inc/utils.hpp"
#include "search.h"
// BODGY: Misdiagnosis in MSVC 17.11: Referencing global constants in the member
// initializer list leads to this warning. Can probably be removed in the future.
#pragma warning(disable : 26493) // Don't use C-style casts (type.4).)
using namespace Microsoft::Console;
using namespace Microsoft::Console::Types;
@@ -98,7 +102,7 @@ void TextBuffer::_reserve(til::size screenBufferSize, const TextAttribute& defau
// 65535*65535 cells would result in a allocSize of 8GiB.
// --> Use uint64_t so that we can safely do our calculations even on x86.
// We allocate 1 additional row, which will be used for GetScratchpadRow().
const auto rowCount = gsl::narrow_cast<uint64_t>(h) + 1;
const auto rowCount = ::base::strict_cast<uint64_t>(h) + 1;
const auto allocSize = gsl::narrow<size_t>(rowCount * rowStride);
// NOTE: Modifications to this block of code might have to be mirrored over to ResizeTraditional().
@@ -447,14 +451,9 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord
cwd.GraphemeNext(state, chars);
col += state.width;
// If we ran out of columns, we need to always return `columnLimit` and not `cols`,
// because if we tried inserting a wide glyph into just 1 remaining column it will
// fail to fit, but that remaining column still has been used up. When the caller sees
// `columns == columnLimit` they will line-wrap and continue inserting into the next row.
if (col > columnLimit)
{
columns = columnLimit;
return dist;
break;
}
dist += state.len;
@@ -462,7 +461,7 @@ size_t TextBuffer::FitTextIntoColumns(const std::wstring_view& chars, til::Coord
// But if we simply ran out of text we just need to return the actual number of columns.
columns = col;
return chars.size();
return dist;
}
// Pretend as if `position` is a regular cursor in the TextBuffer.
@@ -911,7 +910,7 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition, cons
// If the line rendition has changed, the row can no longer be wrapped.
row.SetWrapForced(false);
// And all image content on the row is removed.
row.GetMutableImageSlice().reset();
row.SetImageSlice(nullptr);
// And if it's no longer single width, the right half of the row should be erased.
if (lineRendition != LineRendition::SingleWidth)
{
@@ -2848,6 +2847,15 @@ void TextBuffer::Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const View
}
}
// The for loop right after this if condition will copy entire rows of attributes at a time.
// This assumes of course that the "write cursor" (newX, newY) is at the start of a row.
// If we didn't check for this, we may otherwise copy attributes from a later row into a previous one.
if (newX != 0)
{
newX = 0;
newY++;
}
// Finish copying buffer attributes to remaining rows below the last
// printable character. This is to fix the `color 2f` scenario, where you
// change the buffer colors then resize and everything below the last
@@ -3253,23 +3261,30 @@ MarkExtents TextBuffer::_scrollMarkExtentForRow(const til::CoordType rowOffset,
return mark;
}
std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const
std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset,
const til::CoordType bottomInclusive,
const bool clipAtCursor) const
{
std::wstring commandBuilder;
MarkKind lastMarkKind = MarkKind::Prompt;
const auto cursorPosition = GetCursor().GetPosition();
for (auto y = rowOffset; y <= bottomInclusive; y++)
{
const bool onCursorRow = clipAtCursor && y == cursorPosition.y;
// Now we need to iterate over text attributes. We need to find a
// segment of Prompt attributes, we'll skip those. Then there should be
// Command attributes. Collect up all of those, till we get to the next
// Output attribute.
const auto& row = GetRowByOffset(y);
const auto runs = row.Attributes().runs();
auto x = 0;
for (const auto& [attr, length] : runs)
{
const auto nextX = gsl::narrow_cast<uint16_t>(x + length);
auto nextX = gsl::narrow_cast<uint16_t>(x + length);
if (onCursorRow)
{
nextX = std::min(nextX, gsl::narrow_cast<uint16_t>(cursorPosition.x));
}
const auto markKind{ attr.GetMarkAttributes() };
if (markKind != lastMarkKind)
{
@@ -3289,6 +3304,10 @@ std::wstring TextBuffer::_commandForRow(const til::CoordType rowOffset, const ti
}
// advance to next run of text
x = nextX;
if (onCursorRow && x == cursorPosition.x)
{
return commandBuilder;
}
}
// we went over all the runs in this row, but we're not done yet. Keep iterating on the next row.
}
@@ -3312,7 +3331,7 @@ std::wstring TextBuffer::CurrentCommand() const
// This row did start a prompt! Find the prompt that starts here.
// Presumably, no rows below us will have prompts, so pass in the last
// row with text as the bottom
return _commandForRow(promptY, _estimateOffsetOfLastCommittedRow());
return _commandForRow(promptY, _estimateOffsetOfLastCommittedRow(), true);
}
return L"";
}

View File

@@ -326,7 +326,7 @@ private:
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
void _PruneHyperlinks();
std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const;
std::wstring _commandForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive, const bool clipAtCursor = false) const;
MarkExtents _scrollMarkExtentForRow(const til::CoordType rowOffset, const til::CoordType bottomInclusive) const;
bool _createPromptMarkIfNeeded();

Some files were not shown because too many files have changed in this diff Show More