mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-12 01:01:05 +00:00
Compare commits
50 Commits
dev/miniks
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
068a98c40e | ||
|
|
c0bb37c291 | ||
|
|
9d79c5c3fe | ||
|
|
112cb0ab8d | ||
|
|
d972b5e07a | ||
|
|
48480e6998 | ||
|
|
680577f55c | ||
|
|
1e30b53867 | ||
|
|
c9ac0b7b85 | ||
|
|
7250469dd5 | ||
|
|
176badf36e | ||
|
|
403069ccad | ||
|
|
a3382276d7 | ||
|
|
e05507982d | ||
|
|
f088ae62b3 | ||
|
|
03f805cc0a | ||
|
|
69d99a7a2b | ||
|
|
2afa19fc15 | ||
|
|
27b28edcee | ||
|
|
ca33d895a3 | ||
|
|
cb3bab4ea8 | ||
|
|
0f82811363 | ||
|
|
99fa9460fd | ||
|
|
9f95b54f2c | ||
|
|
1e57dde30b | ||
|
|
5672636568 | ||
|
|
a68fa47e52 | ||
|
|
2f203ff1b3 | ||
|
|
f141d86280 | ||
|
|
e4bb63ce47 | ||
|
|
ae3f8f3759 | ||
|
|
9e9473cfb2 | ||
|
|
ddcdff15d3 | ||
|
|
aaa4943112 | ||
|
|
f7d106d3f3 | ||
|
|
d50409b901 | ||
|
|
f221cd245e | ||
|
|
862793299a | ||
|
|
d392a48857 | ||
|
|
eca0c6b327 | ||
|
|
cddac25726 | ||
|
|
8211ed9fa6 | ||
|
|
c0d704e734 | ||
|
|
7621994b46 | ||
|
|
1d8c5bae35 | ||
|
|
8a9475aeb2 | ||
|
|
ff1337ddb0 | ||
|
|
57c7d1d7ae | ||
|
|
7b9c8c7055 | ||
|
|
860affd608 |
69
NOTICE.md
69
NOTICE.md
@@ -80,7 +80,7 @@ SOFTWARE.
|
||||
|
||||
## chromium/base/numerics
|
||||
|
||||
**Source**:
|
||||
**Source**: https://github.com/chromium/chromium/tree/master/base/numerics
|
||||
|
||||
### License
|
||||
|
||||
@@ -112,4 +112,71 @@ 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.
|
||||
```
|
||||
|
||||
## kimwalisch/libpopcnt
|
||||
|
||||
**Source**: https://github.com/kimwalisch/libpopcnt
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2016 - 2019, Kim Walisch
|
||||
Copyright (c) 2016 - 2019, Wojciech Muła
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. 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.
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
|
||||
## dynamic_bitset
|
||||
|
||||
**Source**: https://github.com/pinam45/dynamic_bitset
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Maxime Pinard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
```
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2020</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>0</VersionMajor>
|
||||
<VersionMinor>10</VersionMinor>
|
||||
<VersionMinor>11</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
21
dep/dynamic_bitset/LICENSE
Normal file
21
dep/dynamic_bitset/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Maxime Pinard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
17
dep/dynamic_bitset/MAINTAINER_README.md
Normal file
17
dep/dynamic_bitset/MAINTAINER_README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
### Notes for Future Maintainers
|
||||
|
||||
This was originally imported by @miniksa in March 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 appropiate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
1. Go to pinam45/dynamic_bitset repository on GitHub.
|
||||
2. Take the entire contents of the include directory wholesale and drop it in the root 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.
|
||||
|
||||
13
dep/dynamic_bitset/cgmanifest.json
Normal file
13
dep/dynamic_bitset/cgmanifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/pinam45/dynamic_bitset",
|
||||
"commitHash": "00f2d066ce9deebf28b006636150e5a882beb83f"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
1944
dep/dynamic_bitset/dynamic_bitset.hpp
Normal file
1944
dep/dynamic_bitset/dynamic_bitset.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2
dep/gsl
2
dep/gsl
Submodule dep/gsl updated: 1212beae77...7e99e76c97
26
dep/libpopcnt/LICENSE
Normal file
26
dep/libpopcnt/LICENSE
Normal file
@@ -0,0 +1,26 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2016 - 2019, Kim Walisch
|
||||
Copyright (c) 2016 - 2019, Wojciech Muła
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. 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.
|
||||
|
||||
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.
|
||||
17
dep/libpopcnt/MAINTAINER_README.md
Normal file
17
dep/libpopcnt/MAINTAINER_README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
### Notes for Future Maintainers
|
||||
|
||||
This was originally imported by @miniksa in March 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 appropiate governance standards.
|
||||
|
||||
## What should be done to update this in the future?
|
||||
|
||||
1. Go to kimwalisch/libpopcnt repository on GitHub.
|
||||
2. Take the `libpopcnt.h` file.
|
||||
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.
|
||||
|
||||
13
dep/libpopcnt/cgmanifest.json
Normal file
13
dep/libpopcnt/cgmanifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/kimwalisch/libpopcnt",
|
||||
"commitHash": "043a99fba31121a70bcb2f589faa17f534ae6085"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
841
dep/libpopcnt/libpopcnt.h
Normal file
841
dep/libpopcnt/libpopcnt.h
Normal file
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
* libpopcnt.h - C/C++ library for counting the number of 1 bits (bit
|
||||
* population count) in an array as quickly as possible using
|
||||
* specialized CPU instructions i.e. POPCNT, AVX2, AVX512, NEON.
|
||||
*
|
||||
* Copyright (c) 2016 - 2019, Kim Walisch
|
||||
* Copyright (c) 2016 - 2018, Wojciech Muła
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LIBPOPCNT_H
|
||||
#define LIBPOPCNT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define GNUC_PREREQ(x, y) \
|
||||
(__GNUC__ > x || (__GNUC__ == x && __GNUC_MINOR__ >= y))
|
||||
#else
|
||||
#define GNUC_PREREQ(x, y) 0
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#define CLANG_PREREQ(x, y) \
|
||||
(__clang_major__ > x || (__clang_major__ == x && __clang_minor__ >= y))
|
||||
#else
|
||||
#define CLANG_PREREQ(x, y) 0
|
||||
#endif
|
||||
|
||||
#if (_MSC_VER < 1900) && \
|
||||
!defined(__cplusplus)
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#if (defined(__i386__) || \
|
||||
defined(__x86_64__) || \
|
||||
defined(_M_IX86) || \
|
||||
defined(_M_X64))
|
||||
#define X86_OR_X64
|
||||
#endif
|
||||
|
||||
#if defined(X86_OR_X64) && \
|
||||
(defined(__cplusplus) || \
|
||||
defined(_MSC_VER) || \
|
||||
(GNUC_PREREQ(4, 2) || \
|
||||
__has_builtin(__sync_val_compare_and_swap)))
|
||||
#define HAVE_CPUID
|
||||
#endif
|
||||
|
||||
#if GNUC_PREREQ(4, 2) || \
|
||||
__has_builtin(__builtin_popcount)
|
||||
#define HAVE_BUILTIN_POPCOUNT
|
||||
#endif
|
||||
|
||||
#if GNUC_PREREQ(4, 2) || \
|
||||
CLANG_PREREQ(3, 0)
|
||||
#define HAVE_ASM_POPCNT
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CPUID) && \
|
||||
(defined(HAVE_ASM_POPCNT) || \
|
||||
defined(_MSC_VER))
|
||||
#define HAVE_POPCNT
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CPUID) && \
|
||||
GNUC_PREREQ(4, 9)
|
||||
#define HAVE_AVX2
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CPUID) && \
|
||||
GNUC_PREREQ(5, 0)
|
||||
#define HAVE_AVX512
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CPUID) && \
|
||||
defined(_MSC_VER) && \
|
||||
defined(__AVX2__)
|
||||
#define HAVE_AVX2
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CPUID) && \
|
||||
defined(_MSC_VER) && \
|
||||
defined(__AVX512__)
|
||||
#define HAVE_AVX512
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CPUID) && \
|
||||
CLANG_PREREQ(3, 8) && \
|
||||
__has_attribute(target) && \
|
||||
(!defined(_MSC_VER) || defined(__AVX2__)) && \
|
||||
(!defined(__apple_build_version__) || __apple_build_version__ >= 8000000)
|
||||
#define HAVE_AVX2
|
||||
#define HAVE_AVX512
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This uses fewer arithmetic operations than any other known
|
||||
* implementation on machines with fast multiplication.
|
||||
* It uses 12 arithmetic operations, one of which is a multiply.
|
||||
* http://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
|
||||
*/
|
||||
static inline uint64_t popcount64(uint64_t x)
|
||||
{
|
||||
uint64_t m1 = 0x5555555555555555ll;
|
||||
uint64_t m2 = 0x3333333333333333ll;
|
||||
uint64_t m4 = 0x0F0F0F0F0F0F0F0Fll;
|
||||
uint64_t h01 = 0x0101010101010101ll;
|
||||
|
||||
x -= (x >> 1) & m1;
|
||||
x = (x & m2) + ((x >> 2) & m2);
|
||||
x = (x + (x >> 4)) & m4;
|
||||
|
||||
return (x * h01) >> 56;
|
||||
}
|
||||
|
||||
#if defined(HAVE_ASM_POPCNT) && \
|
||||
defined(__x86_64__)
|
||||
|
||||
static inline uint64_t popcnt64(uint64_t x)
|
||||
{
|
||||
__asm__ ("popcnt %1, %0" : "=r" (x) : "0" (x));
|
||||
return x;
|
||||
}
|
||||
|
||||
#elif defined(HAVE_ASM_POPCNT) && \
|
||||
defined(__i386__)
|
||||
|
||||
static inline uint32_t popcnt32(uint32_t x)
|
||||
{
|
||||
__asm__ ("popcnt %1, %0" : "=r" (x) : "0" (x));
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline uint64_t popcnt64(uint64_t x)
|
||||
{
|
||||
return popcnt32((uint32_t) x) +
|
||||
popcnt32((uint32_t)(x >> 32));
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER) && \
|
||||
defined(_M_X64)
|
||||
|
||||
#include <nmmintrin.h>
|
||||
|
||||
static inline uint64_t popcnt64(uint64_t x)
|
||||
{
|
||||
return _mm_popcnt_u64(x);
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER) && \
|
||||
defined(_M_IX86)
|
||||
|
||||
#include <nmmintrin.h>
|
||||
|
||||
static inline uint64_t popcnt64(uint64_t x)
|
||||
{
|
||||
return _mm_popcnt_u32((uint32_t) x) +
|
||||
_mm_popcnt_u32((uint32_t)(x >> 32));
|
||||
}
|
||||
|
||||
/* non x86 CPUs */
|
||||
#elif defined(HAVE_BUILTIN_POPCOUNT)
|
||||
|
||||
static inline uint64_t popcnt64(uint64_t x)
|
||||
{
|
||||
return __builtin_popcountll(x);
|
||||
}
|
||||
|
||||
/* no hardware POPCNT,
|
||||
* use pure integer algorithm */
|
||||
#else
|
||||
|
||||
static inline uint64_t popcnt64(uint64_t x)
|
||||
{
|
||||
return popcount64(x);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline uint64_t popcnt64_unrolled(const uint64_t* data, uint64_t size)
|
||||
{
|
||||
uint64_t i = 0;
|
||||
uint64_t limit = size - size % 4;
|
||||
uint64_t cnt = 0;
|
||||
|
||||
for (; i < limit; i += 4)
|
||||
{
|
||||
cnt += popcnt64(data[i+0]);
|
||||
cnt += popcnt64(data[i+1]);
|
||||
cnt += popcnt64(data[i+2]);
|
||||
cnt += popcnt64(data[i+3]);
|
||||
}
|
||||
|
||||
for (; i < size; i++)
|
||||
cnt += popcnt64(data[i]);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#if defined(HAVE_CPUID)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
/* %ecx bit flags */
|
||||
#define bit_POPCNT (1 << 23)
|
||||
|
||||
/* %ebx bit flags */
|
||||
#define bit_AVX2 (1 << 5)
|
||||
#define bit_AVX512 (1 << 30)
|
||||
|
||||
/* xgetbv bit flags */
|
||||
#define XSTATE_SSE (1 << 1)
|
||||
#define XSTATE_YMM (1 << 2)
|
||||
#define XSTATE_ZMM (7 << 5)
|
||||
|
||||
static inline void run_cpuid(int eax, int ecx, int* abcd)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
__cpuidex(abcd, eax, ecx);
|
||||
#else
|
||||
int ebx = 0;
|
||||
int edx = 0;
|
||||
|
||||
#if defined(__i386__) && \
|
||||
defined(__PIC__)
|
||||
/* in case of PIC under 32-bit EBX cannot be clobbered */
|
||||
__asm__ ("movl %%ebx, %%edi;"
|
||||
"cpuid;"
|
||||
"xchgl %%ebx, %%edi;"
|
||||
: "=D" (ebx),
|
||||
"+a" (eax),
|
||||
"+c" (ecx),
|
||||
"=d" (edx));
|
||||
#else
|
||||
__asm__ ("cpuid;"
|
||||
: "+b" (ebx),
|
||||
"+a" (eax),
|
||||
"+c" (ecx),
|
||||
"=d" (edx));
|
||||
#endif
|
||||
|
||||
abcd[0] = eax;
|
||||
abcd[1] = ebx;
|
||||
abcd[2] = ecx;
|
||||
abcd[3] = edx;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(HAVE_AVX2) || \
|
||||
defined(HAVE_AVX512)
|
||||
|
||||
static inline int get_xcr0()
|
||||
{
|
||||
int xcr0;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
xcr0 = (int) _xgetbv(0);
|
||||
#else
|
||||
__asm__ ("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx" );
|
||||
#endif
|
||||
|
||||
return xcr0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline int get_cpuid()
|
||||
{
|
||||
int flags = 0;
|
||||
int abcd[4];
|
||||
|
||||
run_cpuid(1, 0, abcd);
|
||||
|
||||
if ((abcd[2] & bit_POPCNT) == bit_POPCNT)
|
||||
flags |= bit_POPCNT;
|
||||
|
||||
#if defined(HAVE_AVX2) || \
|
||||
defined(HAVE_AVX512)
|
||||
|
||||
int osxsave_mask = (1 << 27);
|
||||
|
||||
/* ensure OS supports extended processor state management */
|
||||
if ((abcd[2] & osxsave_mask) != osxsave_mask)
|
||||
return 0;
|
||||
|
||||
int ymm_mask = XSTATE_SSE | XSTATE_YMM;
|
||||
int zmm_mask = XSTATE_SSE | XSTATE_YMM | XSTATE_ZMM;
|
||||
|
||||
int xcr0 = get_xcr0();
|
||||
|
||||
if ((xcr0 & ymm_mask) == ymm_mask)
|
||||
{
|
||||
run_cpuid(7, 0, abcd);
|
||||
|
||||
if ((abcd[1] & bit_AVX2) == bit_AVX2)
|
||||
flags |= bit_AVX2;
|
||||
|
||||
if ((xcr0 & zmm_mask) == zmm_mask)
|
||||
{
|
||||
if ((abcd[1] & bit_AVX512) == bit_AVX512)
|
||||
flags |= bit_AVX512;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
#endif /* cpuid */
|
||||
|
||||
#if defined(HAVE_AVX2)
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
__attribute__ ((target ("avx2")))
|
||||
#endif
|
||||
static inline void CSA256(__m256i* h, __m256i* l, __m256i a, __m256i b, __m256i c)
|
||||
{
|
||||
__m256i u = _mm256_xor_si256(a, b);
|
||||
*h = _mm256_or_si256(_mm256_and_si256(a, b), _mm256_and_si256(u, c));
|
||||
*l = _mm256_xor_si256(u, c);
|
||||
}
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
__attribute__ ((target ("avx2")))
|
||||
#endif
|
||||
static inline __m256i popcnt256(__m256i v)
|
||||
{
|
||||
__m256i lookup1 = _mm256_setr_epi8(
|
||||
4, 5, 5, 6, 5, 6, 6, 7,
|
||||
5, 6, 6, 7, 6, 7, 7, 8,
|
||||
4, 5, 5, 6, 5, 6, 6, 7,
|
||||
5, 6, 6, 7, 6, 7, 7, 8
|
||||
);
|
||||
|
||||
__m256i lookup2 = _mm256_setr_epi8(
|
||||
4, 3, 3, 2, 3, 2, 2, 1,
|
||||
3, 2, 2, 1, 2, 1, 1, 0,
|
||||
4, 3, 3, 2, 3, 2, 2, 1,
|
||||
3, 2, 2, 1, 2, 1, 1, 0
|
||||
);
|
||||
|
||||
__m256i low_mask = _mm256_set1_epi8(0x0f);
|
||||
__m256i lo = _mm256_and_si256(v, low_mask);
|
||||
__m256i hi = _mm256_and_si256(_mm256_srli_epi16(v, 4), low_mask);
|
||||
__m256i popcnt1 = _mm256_shuffle_epi8(lookup1, lo);
|
||||
__m256i popcnt2 = _mm256_shuffle_epi8(lookup2, hi);
|
||||
|
||||
return _mm256_sad_epu8(popcnt1, popcnt2);
|
||||
}
|
||||
|
||||
/*
|
||||
* AVX2 Harley-Seal popcount (4th iteration).
|
||||
* The algorithm is based on the paper "Faster Population Counts
|
||||
* using AVX2 Instructions" by Daniel Lemire, Nathan Kurz and
|
||||
* Wojciech Mula (23 Nov 2016).
|
||||
* @see https://arxiv.org/abs/1611.07612
|
||||
*/
|
||||
#if !defined(_MSC_VER)
|
||||
__attribute__ ((target ("avx2")))
|
||||
#endif
|
||||
static inline uint64_t popcnt_avx2(const __m256i* data, uint64_t size)
|
||||
{
|
||||
__m256i cnt = _mm256_setzero_si256();
|
||||
__m256i ones = _mm256_setzero_si256();
|
||||
__m256i twos = _mm256_setzero_si256();
|
||||
__m256i fours = _mm256_setzero_si256();
|
||||
__m256i eights = _mm256_setzero_si256();
|
||||
__m256i sixteens = _mm256_setzero_si256();
|
||||
__m256i twosA, twosB, foursA, foursB, eightsA, eightsB;
|
||||
|
||||
uint64_t i = 0;
|
||||
uint64_t limit = size - size % 16;
|
||||
uint64_t* cnt64;
|
||||
|
||||
for(; i < limit; i += 16)
|
||||
{
|
||||
CSA256(&twosA, &ones, ones, data[i+0], data[i+1]);
|
||||
CSA256(&twosB, &ones, ones, data[i+2], data[i+3]);
|
||||
CSA256(&foursA, &twos, twos, twosA, twosB);
|
||||
CSA256(&twosA, &ones, ones, data[i+4], data[i+5]);
|
||||
CSA256(&twosB, &ones, ones, data[i+6], data[i+7]);
|
||||
CSA256(&foursB, &twos, twos, twosA, twosB);
|
||||
CSA256(&eightsA, &fours, fours, foursA, foursB);
|
||||
CSA256(&twosA, &ones, ones, data[i+8], data[i+9]);
|
||||
CSA256(&twosB, &ones, ones, data[i+10], data[i+11]);
|
||||
CSA256(&foursA, &twos, twos, twosA, twosB);
|
||||
CSA256(&twosA, &ones, ones, data[i+12], data[i+13]);
|
||||
CSA256(&twosB, &ones, ones, data[i+14], data[i+15]);
|
||||
CSA256(&foursB, &twos, twos, twosA, twosB);
|
||||
CSA256(&eightsB, &fours, fours, foursA, foursB);
|
||||
CSA256(&sixteens, &eights, eights, eightsA, eightsB);
|
||||
|
||||
cnt = _mm256_add_epi64(cnt, popcnt256(sixteens));
|
||||
}
|
||||
|
||||
cnt = _mm256_slli_epi64(cnt, 4);
|
||||
cnt = _mm256_add_epi64(cnt, _mm256_slli_epi64(popcnt256(eights), 3));
|
||||
cnt = _mm256_add_epi64(cnt, _mm256_slli_epi64(popcnt256(fours), 2));
|
||||
cnt = _mm256_add_epi64(cnt, _mm256_slli_epi64(popcnt256(twos), 1));
|
||||
cnt = _mm256_add_epi64(cnt, popcnt256(ones));
|
||||
|
||||
for(; i < size; i++)
|
||||
cnt = _mm256_add_epi64(cnt, popcnt256(data[i]));
|
||||
|
||||
cnt64 = (uint64_t*) &cnt;
|
||||
|
||||
return cnt64[0] +
|
||||
cnt64[1] +
|
||||
cnt64[2] +
|
||||
cnt64[3];
|
||||
}
|
||||
|
||||
/* Align memory to 32 bytes boundary */
|
||||
static inline void align_avx2(const uint8_t** p, uint64_t* size, uint64_t* cnt)
|
||||
{
|
||||
for (; (uintptr_t) *p % 8; (*p)++)
|
||||
{
|
||||
*cnt += popcnt64(**p);
|
||||
*size -= 1;
|
||||
}
|
||||
for (; (uintptr_t) *p % 32; (*p) += 8)
|
||||
{
|
||||
*cnt += popcnt64(
|
||||
*(const uint64_t*) *p);
|
||||
*size -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_AVX512)
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
__attribute__ ((target ("avx512bw")))
|
||||
#endif
|
||||
static inline __m512i popcnt512(__m512i v)
|
||||
{
|
||||
__m512i m1 = _mm512_set1_epi8(0x55);
|
||||
__m512i m2 = _mm512_set1_epi8(0x33);
|
||||
__m512i m4 = _mm512_set1_epi8(0x0F);
|
||||
__m512i t1 = _mm512_sub_epi8(v, (_mm512_srli_epi16(v, 1) & m1));
|
||||
__m512i t2 = _mm512_add_epi8(t1 & m2, (_mm512_srli_epi16(t1, 2) & m2));
|
||||
__m512i t3 = _mm512_add_epi8(t2, _mm512_srli_epi16(t2, 4)) & m4;
|
||||
|
||||
return _mm512_sad_epu8(t3, _mm512_setzero_si512());
|
||||
}
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
__attribute__ ((target ("avx512bw")))
|
||||
#endif
|
||||
static inline void CSA512(__m512i* h, __m512i* l, __m512i a, __m512i b, __m512i c)
|
||||
{
|
||||
*l = _mm512_ternarylogic_epi32(c, b, a, 0x96);
|
||||
*h = _mm512_ternarylogic_epi32(c, b, a, 0xe8);
|
||||
}
|
||||
|
||||
/*
|
||||
* AVX512 Harley-Seal popcount (4th iteration).
|
||||
* The algorithm is based on the paper "Faster Population Counts
|
||||
* using AVX2 Instructions" by Daniel Lemire, Nathan Kurz and
|
||||
* Wojciech Mula (23 Nov 2016).
|
||||
* @see https://arxiv.org/abs/1611.07612
|
||||
*/
|
||||
#if !defined(_MSC_VER)
|
||||
__attribute__ ((target ("avx512bw")))
|
||||
#endif
|
||||
static inline uint64_t popcnt_avx512(const __m512i* data, const uint64_t size)
|
||||
{
|
||||
__m512i cnt = _mm512_setzero_si512();
|
||||
__m512i ones = _mm512_setzero_si512();
|
||||
__m512i twos = _mm512_setzero_si512();
|
||||
__m512i fours = _mm512_setzero_si512();
|
||||
__m512i eights = _mm512_setzero_si512();
|
||||
__m512i sixteens = _mm512_setzero_si512();
|
||||
__m512i twosA, twosB, foursA, foursB, eightsA, eightsB;
|
||||
|
||||
uint64_t i = 0;
|
||||
uint64_t limit = size - size % 16;
|
||||
uint64_t* cnt64;
|
||||
|
||||
for(; i < limit; i += 16)
|
||||
{
|
||||
CSA512(&twosA, &ones, ones, data[i+0], data[i+1]);
|
||||
CSA512(&twosB, &ones, ones, data[i+2], data[i+3]);
|
||||
CSA512(&foursA, &twos, twos, twosA, twosB);
|
||||
CSA512(&twosA, &ones, ones, data[i+4], data[i+5]);
|
||||
CSA512(&twosB, &ones, ones, data[i+6], data[i+7]);
|
||||
CSA512(&foursB, &twos, twos, twosA, twosB);
|
||||
CSA512(&eightsA, &fours, fours, foursA, foursB);
|
||||
CSA512(&twosA, &ones, ones, data[i+8], data[i+9]);
|
||||
CSA512(&twosB, &ones, ones, data[i+10], data[i+11]);
|
||||
CSA512(&foursA, &twos, twos, twosA, twosB);
|
||||
CSA512(&twosA, &ones, ones, data[i+12], data[i+13]);
|
||||
CSA512(&twosB, &ones, ones, data[i+14], data[i+15]);
|
||||
CSA512(&foursB, &twos, twos, twosA, twosB);
|
||||
CSA512(&eightsB, &fours, fours, foursA, foursB);
|
||||
CSA512(&sixteens, &eights, eights, eightsA, eightsB);
|
||||
|
||||
cnt = _mm512_add_epi64(cnt, popcnt512(sixteens));
|
||||
}
|
||||
|
||||
cnt = _mm512_slli_epi64(cnt, 4);
|
||||
cnt = _mm512_add_epi64(cnt, _mm512_slli_epi64(popcnt512(eights), 3));
|
||||
cnt = _mm512_add_epi64(cnt, _mm512_slli_epi64(popcnt512(fours), 2));
|
||||
cnt = _mm512_add_epi64(cnt, _mm512_slli_epi64(popcnt512(twos), 1));
|
||||
cnt = _mm512_add_epi64(cnt, popcnt512(ones));
|
||||
|
||||
for(; i < size; i++)
|
||||
cnt = _mm512_add_epi64(cnt, popcnt512(data[i]));
|
||||
|
||||
cnt64 = (uint64_t*) &cnt;
|
||||
|
||||
return cnt64[0] +
|
||||
cnt64[1] +
|
||||
cnt64[2] +
|
||||
cnt64[3] +
|
||||
cnt64[4] +
|
||||
cnt64[5] +
|
||||
cnt64[6] +
|
||||
cnt64[7];
|
||||
}
|
||||
|
||||
/* Align memory to 64 bytes boundary */
|
||||
static inline void align_avx512(const uint8_t** p, uint64_t* size, uint64_t* cnt)
|
||||
{
|
||||
for (; (uintptr_t) *p % 8; (*p)++)
|
||||
{
|
||||
*cnt += popcnt64(**p);
|
||||
*size -= 1;
|
||||
}
|
||||
for (; (uintptr_t) *p % 64; (*p) += 8)
|
||||
{
|
||||
*cnt += popcnt64(
|
||||
*(const uint64_t*) *p);
|
||||
*size -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* x86 CPUs */
|
||||
#if defined(X86_OR_X64)
|
||||
|
||||
/* Align memory to 8 bytes boundary */
|
||||
static inline void align_8(const uint8_t** p, uint64_t* size, uint64_t* cnt)
|
||||
{
|
||||
for (; *size > 0 && (uintptr_t) *p % 8; (*p)++)
|
||||
{
|
||||
*cnt += popcount64(**p);
|
||||
*size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t popcount64_unrolled(const uint64_t* data, uint64_t size)
|
||||
{
|
||||
uint64_t i = 0;
|
||||
uint64_t limit = size - size % 4;
|
||||
uint64_t cnt = 0;
|
||||
|
||||
for (; i < limit; i += 4)
|
||||
{
|
||||
cnt += popcount64(data[i+0]);
|
||||
cnt += popcount64(data[i+1]);
|
||||
cnt += popcount64(data[i+2]);
|
||||
cnt += popcount64(data[i+3]);
|
||||
}
|
||||
|
||||
for (; i < size; i++)
|
||||
cnt += popcount64(data[i]);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the number of 1 bits in the data array
|
||||
* @data: An array
|
||||
* @size: Size of data in bytes
|
||||
*/
|
||||
static inline uint64_t popcnt(const void* data, uint64_t size)
|
||||
{
|
||||
const uint8_t* ptr = (const uint8_t*) data;
|
||||
uint64_t cnt = 0;
|
||||
uint64_t i;
|
||||
|
||||
#if defined(HAVE_CPUID)
|
||||
#if defined(__cplusplus)
|
||||
/* C++11 thread-safe singleton */
|
||||
static const int cpuid = get_cpuid();
|
||||
#else
|
||||
static int cpuid_ = -1;
|
||||
int cpuid = cpuid_;
|
||||
if (cpuid == -1)
|
||||
{
|
||||
cpuid = get_cpuid();
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
_InterlockedCompareExchange(&cpuid_, cpuid, -1);
|
||||
#else
|
||||
__sync_val_compare_and_swap(&cpuid_, -1, cpuid);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_AVX512)
|
||||
|
||||
/* AVX512 requires arrays >= 1024 bytes */
|
||||
if ((cpuid & bit_AVX512) &&
|
||||
size >= 1024)
|
||||
{
|
||||
align_avx512(&ptr, &size, &cnt);
|
||||
cnt += popcnt_avx512((const __m512i*) ptr, size / 64);
|
||||
ptr += size - size % 64;
|
||||
size = size % 64;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_AVX2)
|
||||
|
||||
/* AVX2 requires arrays >= 512 bytes */
|
||||
if ((cpuid & bit_AVX2) &&
|
||||
size >= 512)
|
||||
{
|
||||
align_avx2(&ptr, &size, &cnt);
|
||||
cnt += popcnt_avx2((const __m256i*) ptr, size / 32);
|
||||
ptr += size - size % 32;
|
||||
size = size % 32;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_POPCNT)
|
||||
|
||||
if (cpuid & bit_POPCNT)
|
||||
{
|
||||
cnt += popcnt64_unrolled((const uint64_t*) ptr, size / 8);
|
||||
ptr += size - size % 8;
|
||||
size = size % 8;
|
||||
for (i = 0; i < size; i++)
|
||||
cnt += popcnt64(ptr[i]);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* pure integer popcount algorithm */
|
||||
if (size >= 8)
|
||||
{
|
||||
align_8(&ptr, &size, &cnt);
|
||||
cnt += popcount64_unrolled((const uint64_t*) ptr, size / 8);
|
||||
ptr += size - size % 8;
|
||||
size = size % 8;
|
||||
}
|
||||
|
||||
/* pure integer popcount algorithm */
|
||||
for (i = 0; i < size; i++)
|
||||
cnt += popcount64(ptr[i]);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#elif defined(__ARM_NEON) || \
|
||||
defined(__aarch64__)
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
/* Align memory to 8 bytes boundary */
|
||||
static inline void align_8(const uint8_t** p, uint64_t* size, uint64_t* cnt)
|
||||
{
|
||||
for (; *size > 0 && (uintptr_t) *p % 8; (*p)++)
|
||||
{
|
||||
*cnt += popcnt64(**p);
|
||||
*size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64x2_t vpadalq(uint64x2_t sum, uint8x16_t t)
|
||||
{
|
||||
return vpadalq_u32(sum, vpaddlq_u16(vpaddlq_u8(t)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the number of 1 bits in the data array
|
||||
* @data: An array
|
||||
* @size: Size of data in bytes
|
||||
*/
|
||||
static inline uint64_t popcnt(const void* data, uint64_t size)
|
||||
{
|
||||
uint64_t cnt = 0;
|
||||
uint64_t chunk_size = 64;
|
||||
const uint8_t* ptr = (const uint8_t*) data;
|
||||
|
||||
if (size >= chunk_size)
|
||||
{
|
||||
uint64_t i = 0;
|
||||
uint64_t iters = size / chunk_size;
|
||||
uint64x2_t sum = vcombine_u64(vcreate_u64(0), vcreate_u64(0));
|
||||
uint8x16_t zero = vcombine_u8(vcreate_u8(0), vcreate_u8(0));
|
||||
|
||||
do
|
||||
{
|
||||
uint8x16_t t0 = zero;
|
||||
uint8x16_t t1 = zero;
|
||||
uint8x16_t t2 = zero;
|
||||
uint8x16_t t3 = zero;
|
||||
|
||||
/*
|
||||
* After every 31 iterations we need to add the
|
||||
* temporary sums (t0, t1, t2, t3) to the total sum.
|
||||
* We must ensure that the temporary sums <= 255
|
||||
* and 31 * 8 bits = 248 which is OK.
|
||||
*/
|
||||
uint64_t limit = (i + 31 < iters) ? i + 31 : iters;
|
||||
|
||||
/* Each iteration processes 64 bytes */
|
||||
for (; i < limit; i++)
|
||||
{
|
||||
uint8x16x4_t input = vld4q_u8(ptr);
|
||||
ptr += chunk_size;
|
||||
|
||||
t0 = vaddq_u8(t0, vcntq_u8(input.val[0]));
|
||||
t1 = vaddq_u8(t1, vcntq_u8(input.val[1]));
|
||||
t2 = vaddq_u8(t2, vcntq_u8(input.val[2]));
|
||||
t3 = vaddq_u8(t3, vcntq_u8(input.val[3]));
|
||||
}
|
||||
|
||||
sum = vpadalq(sum, t0);
|
||||
sum = vpadalq(sum, t1);
|
||||
sum = vpadalq(sum, t2);
|
||||
sum = vpadalq(sum, t3);
|
||||
}
|
||||
while (i < iters);
|
||||
|
||||
uint64_t tmp[2];
|
||||
vst1q_u64(tmp, sum);
|
||||
cnt += tmp[0];
|
||||
cnt += tmp[1];
|
||||
}
|
||||
|
||||
size %= chunk_size;
|
||||
align_8(&ptr, &size, &cnt);
|
||||
const uint64_t* ptr64 = (const uint64_t*) ptr;
|
||||
uint64_t iters = size / 8;
|
||||
|
||||
for (uint64_t i = 0; i < iters; i++)
|
||||
cnt += popcnt64(ptr64[i]);
|
||||
|
||||
ptr += size - size % 8;
|
||||
size = size % 8;
|
||||
|
||||
for (uint64_t i = 0; i < size; i++)
|
||||
cnt += popcnt64(ptr[i]);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* all other CPUs */
|
||||
#else
|
||||
|
||||
/* Align memory to 8 bytes boundary */
|
||||
static inline void align_8(const uint8_t** p, uint64_t* size, uint64_t* cnt)
|
||||
{
|
||||
for (; *size > 0 && (uintptr_t) *p % 8; (*p)++)
|
||||
{
|
||||
*cnt += popcnt64(**p);
|
||||
*size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the number of 1 bits in the data array
|
||||
* @data: An array
|
||||
* @size: Size of data in bytes
|
||||
*/
|
||||
static inline uint64_t popcnt(const void* data, uint64_t size)
|
||||
{
|
||||
const uint8_t* ptr = (const uint8_t*) data;
|
||||
uint64_t cnt = 0;
|
||||
uint64_t i;
|
||||
|
||||
align_8(&ptr, &size, &cnt);
|
||||
cnt += popcnt64_unrolled((const uint64_t*) ptr, size / 8);
|
||||
ptr += size - size % 8;
|
||||
size = size % 8;
|
||||
for (i = 0; i < size; i++)
|
||||
cnt += popcnt64(ptr[i]);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LIBPOPCNT_H */
|
||||
@@ -57,3 +57,27 @@ Openconsole has three configuration types:
|
||||
- AuditMode
|
||||
|
||||
AuditMode is an experimental mode that enables some additional static analysis from CppCoreCheck.
|
||||
|
||||
## Updating Nuget package references
|
||||
Certain Nuget package references in this project, like `Microsoft.UI.Xaml`, must be updated outside of the Visual Studio NuGet package manager. This can be done using the snippet below.
|
||||
> Note that to run this snippet, you need to use WSL as the command uses `sed`.
|
||||
To update the version of a given package, use the following snippet
|
||||
|
||||
`git grep -z -l $PackageName | xargs -0 sed -i -e 's/$OldVersionNumber/$NewVersionNumber/g'`
|
||||
|
||||
where:
|
||||
- `$PackageName` is the name of the package, e.g. Microsoft.UI.Xaml
|
||||
- `$OldVersionNumber` is the version number currently used, e.g. 2.3.191217003-prerelease
|
||||
- `$NewVersionNumber` is the version number you want to migrate to, e.g. 2.4.200117003-prerelease
|
||||
|
||||
Example usage:
|
||||
|
||||
`git grep -z -l Microsoft.UI.Xaml | xargs -0 sed -i -e 's/2.3.191217003-prerelease/2.4.200117003-prerelease/g'`
|
||||
|
||||
## Using .nupkg files instead of downloaded Nuget packages
|
||||
If you want to use .nupkg files instead of the downloaded Nuget package, you can do this with the following steps:
|
||||
|
||||
1. Open the Nuget.config file and uncomment line 8 ("Static Package Dependencies")
|
||||
2. Create the folder /dep/packages
|
||||
3. Put your .nupkg files in /dep/packages
|
||||
4. If you are using different versions than those already being used, you need to update the references as well. How to do that is explained under "Updating Nuget package references".
|
||||
|
||||
@@ -39,7 +39,7 @@ Properties listed below are specific to each unique profile.
|
||||
| `colorScheme` | Optional | String | `Campbell` | Name of the terminal color scheme to use. Color schemes are defined under `schemes`. |
|
||||
| `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Array follows the format defined in `schemes`. |
|
||||
| `commandline` | Optional | String | | Executable used in the profile. |
|
||||
| `cursorColor` | Optional | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. |
|
||||
| `cursorColor` | Optional | String | | Sets the cursor color of the profile. Overrides `cursorColor` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. |
|
||||
| `cursorShape` | Optional | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) |
|
||||
| `fontFace` | Optional | String | `Consolas` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
|
||||
@@ -68,6 +68,7 @@ Properties listed below are specific to each color scheme. [ColorTool](https://g
|
||||
| `foreground` | _Required_ | String | Sets the foreground color of the color scheme. |
|
||||
| `background` | _Required_ | String | Sets the background color of the color scheme. |
|
||||
| `selectionBackground` | Optional | String | Sets the selection background color of the color scheme. |
|
||||
| `cursorColor` | Optional | String | Sets the cursor color of the color scheme. |
|
||||
| `black` | _Required_ | String | Sets the color used as ANSI black. |
|
||||
| `blue` | _Required_ | String | Sets the color used as ANSI blue. |
|
||||
| `brightBlack` | _Required_ | String | Sets the color used as ANSI bright black. |
|
||||
@@ -110,31 +111,31 @@ For commands with arguments:
|
||||
|
||||
| Command | Command Description | Action (*=required) | Action Arguments | Argument Descriptions |
|
||||
| ------- | ------------------- | ------ | ---------------- | ----------------- |
|
||||
| closePane | Close the active pane. | | | |
|
||||
| closeTab | Close the current tab. | | | |
|
||||
| closeWindow | Close the current window and all tabs within it. | | | |
|
||||
| copy | Copy the selected terminal content to your Windows Clipboard. | `trimWhitespace` | boolean | When `true`, newlines persist from the selected text. When `false`, copied content will paste on one line. |
|
||||
| decreaseFontSize | Make the text smaller by one delta. | `delta` | integer | Amount of size decrease per command invocation. |
|
||||
| duplicateTab | Make a copy and open the current tab. | | | |
|
||||
| find | Open the search dialog box. | | | |
|
||||
| increaseFontSize | Make the text larger by one delta. | `delta` | integer | Amount of size increase per command invocation. |
|
||||
| moveFocus | Focus on a different pane depending on direction. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the focus will move. |
|
||||
| newTab | Create a new tab. Without any arguments, this will open the default profile in a new tab. | 1. `commandLine`<br>2. `startingDirectory`<br>3. `tabTitle`<br>4. `index`<br>5. `profile` | 1. string<br>2. string<br>3. string<br>4. integer<br>5. string | 1. Executable run within the tab.<br>2. Directory in which the tab will open.<br>3. Title of the new tab.<br>4. Profile that will open based on its position in the dropdown (starting at 0).<br>5. Profile that will open based on its GUID or name. |
|
||||
| nextTab | Open the tab to the right of the current one. | | | |
|
||||
| openNewTabDropdown | Open the dropdown menu. | | | |
|
||||
| openSettings | Open the settings file. | | | |
|
||||
| paste | Insert the content that was copied onto the clipboard. | | | |
|
||||
| prevTab | Open the tab to the left of the current one. | | | |
|
||||
| resetFontSize | Reset the text size to the default value. | | | |
|
||||
| resizePane | Change the size of the active pane. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the pane will be resized. |
|
||||
| scrollDown | Move the screen down. | | | |
|
||||
| scrollUp | Move the screen up. | | | |
|
||||
| scrollUpPage | Move the screen up a whole page. | | | |
|
||||
| scrollDownPage | Move the screen down a whole page. | | | |
|
||||
| splitPane | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name. |
|
||||
| switchToTab | Open a specific tab depending on index. | `index`* | integer | Tab that will open based on its position in the tab bar (starting at 0). |
|
||||
| toggleFullscreen | Switch between fullscreen and default window sizes. | | | |
|
||||
| unbound | Unbind the associated keys from any command. | | | |
|
||||
| `closePane` | Close the active pane. | | | |
|
||||
| `closeTab` | Close the current tab. | | | |
|
||||
| `closeWindow` | Close the current window and all tabs within it. | | | |
|
||||
| `copy` | Copy the selected terminal content to your Windows Clipboard. | `trimWhitespace` | boolean | When `true`, newlines persist from the selected text. When `false`, copied content will paste on one line. |
|
||||
| `decreaseFontSize` | Make the text smaller by one delta. | `delta` | integer | Amount of size decrease per command invocation. |
|
||||
| `duplicateTab` | Make a copy and open the current tab. | | | |
|
||||
| `find` | Open the search dialog box. | | | |
|
||||
| `increaseFontSize` | Make the text larger by one delta. | `delta` | integer | Amount of size increase per command invocation. |
|
||||
| `moveFocus` | Focus on a different pane depending on direction. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the focus will move. |
|
||||
| `newTab` | Create a new tab. Without any arguments, this will open the default profile in a new tab. | 1. `commandLine`<br>2. `startingDirectory`<br>3. `tabTitle`<br>4. `index`<br>5. `profile` | 1. string<br>2. string<br>3. string<br>4. integer<br>5. string | 1. Executable run within the tab.<br>2. Directory in which the tab will open.<br>3. Title of the new tab.<br>4. Profile that will open based on its position in the dropdown (starting at 0).<br>5. Profile that will open based on its GUID or name. |
|
||||
| `nextTab` | Open the tab to the right of the current one. | | | |
|
||||
| `openNewTabDropdown` | Open the dropdown menu. | | | |
|
||||
| `openSettings` | Open the settings file. | | | |
|
||||
| `paste` | Insert the content that was copied onto the clipboard. | | | |
|
||||
| `prevTab` | Open the tab to the left of the current one. | | | |
|
||||
| `resetFontSize` | Reset the text size to the default value. | | | |
|
||||
| `resizePane` | Change the size of the active pane. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the pane will be resized. |
|
||||
| `scrollDown` | Move the screen down. | | | |
|
||||
| `scrollUp` | Move the screen up. | | | |
|
||||
| `scrollUpPage` | Move the screen up a whole page. | | | |
|
||||
| `scrollDownPage` | Move the screen down a whole page. | | | |
|
||||
| `splitPane` | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name. |
|
||||
| `switchToTab` | Open a specific tab depending on index. | `index`* | integer | Tab that will open based on its position in the tab bar (starting at 0). |
|
||||
| `toggleFullscreen` | Switch between fullscreen and default window sizes. | | | |
|
||||
| `unbound` | Unbind the associated keys from any command. | | | |
|
||||
|
||||
### Accepted Modifiers and Keys
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema'",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)\\+?((ctrl|alt|shift)(?<!\\2)\\+?)?((ctrl|alt|shift)(?<!\\2|\\4))?\\+?)?(?<key>[^+\\s]+?)?(?<=[^+\\s])$",
|
||||
"type": "string"
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)(?:\\+(ctrl|alt|shift)(?<!\\2))?(?:\\+(ctrl|alt|shift)(?<!\\2|\\3))?\\+)?(?<key>[^\\s+]|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
"type": "string",
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
},
|
||||
"Color": {
|
||||
"default": "#",
|
||||
@@ -554,8 +555,7 @@
|
||||
},
|
||||
"cursorColor": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#FFFFFF",
|
||||
"description": "Sets the cursor color for the profile. Uses hex color format: \"#rrggbb\"."
|
||||
"description": "Sets the cursor color of the profile. Overrides cursor color set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"cursorHeight": {
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 25-100.",
|
||||
@@ -746,6 +746,11 @@
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright yellow."
|
||||
},
|
||||
"cursorColor": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#FFFFFF",
|
||||
"description": "Sets the cursor color of the color scheme."
|
||||
},
|
||||
"cyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI cyan."
|
||||
|
||||
@@ -30,6 +30,7 @@ Assuming that you've installed cmder into `%CMDER_ROOT%`:
|
||||
{
|
||||
"commandline" : "cmd.exe /k \"%CMDER_ROOT%\\vendor\\init.bat\"",
|
||||
"name" : "cmder",
|
||||
"icon" : "%CMDER_ROOT%/icons/cmder.ico",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
```
|
||||
@@ -77,4 +78,17 @@ Assuming that you've installed Git Bash into `C:/Program Files/Git`:
|
||||
}
|
||||
````
|
||||
|
||||
## MSYS2
|
||||
|
||||
Assuming that you've installed MSYS2 into `C:/msys64`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name" : "MSYS2",
|
||||
"commandline" : "C:/msys64/msys2_shell.cmd -defterm -no-start -mingw64",
|
||||
"icon": "C:/msys64/msys2.ico",
|
||||
"startingDirectory" : "C:/msys64/home/user"
|
||||
}
|
||||
````
|
||||
|
||||
<!-- Adding a tool here? Make sure to add it in alphabetical order! -->
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
RowCellIterator(const ROW& row, const size_t start, const size_t length);
|
||||
~RowCellIterator() = default;
|
||||
|
||||
RowCellIterator& operator=(const RowCellIterator& it) = default;
|
||||
RowCellIterator& operator=(const RowCellIterator& it) = delete;
|
||||
|
||||
operator bool() const noexcept;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
Cursor& operator=(const Cursor&) & = delete;
|
||||
|
||||
Cursor(Cursor&&) = default;
|
||||
Cursor& operator=(Cursor&&) & = default;
|
||||
Cursor& operator=(Cursor&&) & = delete;
|
||||
|
||||
bool HasMoved() const noexcept;
|
||||
bool IsVisible() const noexcept;
|
||||
|
||||
@@ -1260,6 +1260,94 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the beginning of the current glyph/character. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a COORD on the word you are currently on
|
||||
// Return Value:
|
||||
// - pos - The COORD for the first cell of the current glyph (inclusive)
|
||||
const til::point TextBuffer::GetGlyphStart(const til::point pos) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
|
||||
const auto bufferSize = GetSize();
|
||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
|
||||
{
|
||||
bufferSize.DecrementInBounds(resultPos, true);
|
||||
}
|
||||
|
||||
return resultPos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the end of the current glyph/character. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a COORD on the word you are currently on
|
||||
// Return Value:
|
||||
// - pos - The COORD for the last cell of the current glyph (exclusive)
|
||||
const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
|
||||
const auto bufferSize = GetSize();
|
||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
|
||||
{
|
||||
bufferSize.IncrementInBounds(resultPos, true);
|
||||
}
|
||||
|
||||
// increment one more time to become exclusive
|
||||
bufferSize.IncrementInBounds(resultPos, true);
|
||||
return resultPos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the beginning of the next glyph/character. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a COORD on the word you are currently on
|
||||
// - allowBottomExclusive - allow the nonexistent end-of-buffer cell to be encountered
|
||||
// Return Value:
|
||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||
// - pos - The COORD for the first cell of the current glyph (inclusive)
|
||||
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
|
||||
// try to move. If we can't, we're done.
|
||||
const auto bufferSize = GetSize();
|
||||
const bool success = bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
|
||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
|
||||
{
|
||||
bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
|
||||
}
|
||||
|
||||
pos = resultPos;
|
||||
return success;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the beginning of the previous glyph/character. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a COORD on the word you are currently on
|
||||
// - allowBottomExclusive - allow the nonexistent end-of-buffer cell to be encountered
|
||||
// Return Value:
|
||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||
// - pos - The COORD for the first cell of the previous glyph (inclusive)
|
||||
bool TextBuffer::MoveToPreviousGlyph(til::point& pos, bool allowBottomExclusive) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
|
||||
// try to move. If we can't, we're done.
|
||||
const auto bufferSize = GetSize();
|
||||
const bool success = bufferSize.DecrementInBounds(resultPos, allowBottomExclusive);
|
||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
|
||||
{
|
||||
bufferSize.DecrementInBounds(resultPos, allowBottomExclusive);
|
||||
}
|
||||
|
||||
pos = resultPos;
|
||||
return success;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines the line-by-line rectangles based on two COORDs
|
||||
// - expands the rectangles to support wide glyphs
|
||||
|
||||
@@ -134,6 +134,11 @@ public:
|
||||
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const;
|
||||
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
|
||||
|
||||
const til::point GetGlyphStart(const til::point pos) const;
|
||||
const til::point GetGlyphEnd(const til::point pos) const;
|
||||
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
||||
bool MoveToPreviousGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
||||
|
||||
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
|
||||
|
||||
class TextAndColor
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>ms-resource:AppName</DisplayName>
|
||||
<DisplayName>Windows Terminal</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
@@ -100,6 +100,7 @@ namespace TerminalAppLocalTests
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010100",
|
||||
"cursorColor": "#010001",
|
||||
"red": "#010000",
|
||||
"green": "#000100",
|
||||
"blue": "#000001"
|
||||
@@ -109,6 +110,7 @@ namespace TerminalAppLocalTests
|
||||
"foreground": "#020202",
|
||||
"background": "#030303",
|
||||
"selectionBackground": "#020200",
|
||||
"cursorColor": "#040004",
|
||||
"red": "#020000",
|
||||
|
||||
"blue": "#000002"
|
||||
@@ -118,6 +120,7 @@ namespace TerminalAppLocalTests
|
||||
"foreground": "#040404",
|
||||
"background": "#050505",
|
||||
"selectionBackground": "#030300",
|
||||
"cursorColor": "#060006",
|
||||
"red": "#030000",
|
||||
"green": "#000300"
|
||||
})" };
|
||||
@@ -130,8 +133,8 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"scheme0", scheme0._schemeName);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 0), scheme0._selectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 1), scheme0._cursorColor);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 0), scheme0._table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0._table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 1), scheme0._table[XTERM_BLUE_ATTR]);
|
||||
@@ -143,6 +146,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 0), scheme0._selectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 0, 4), scheme0._cursorColor);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 0, 0), scheme0._table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0._table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0._table[XTERM_BLUE_ATTR]);
|
||||
@@ -154,6 +158,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 0), scheme0._selectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 6, 0, 6), scheme0._cursorColor);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 0, 0), scheme0._table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 3, 0), scheme0._table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0._table[XTERM_BLUE_ATTR]);
|
||||
|
||||
@@ -70,6 +70,8 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TEST_METHOD(TestTerminalArgsForBinding);
|
||||
|
||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||
|
||||
TEST_METHOD(ValidateKeybindingsWarnings);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
@@ -1468,7 +1470,7 @@ namespace TerminalAppLocalTests
|
||||
CascadiaSettings settings{};
|
||||
settings._ParseJsonString(settingsJson, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_IS_FALSE(settings._profiles.empty(), 0);
|
||||
VERIFY_IS_FALSE(settings._profiles.empty());
|
||||
VERIFY_ARE_EQUAL(expectedPath, settings._profiles[0].GetExpandedIconPath());
|
||||
}
|
||||
void SettingsTests::TestProfileBackgroundImageWithEnvVar()
|
||||
@@ -1489,7 +1491,7 @@ namespace TerminalAppLocalTests
|
||||
CascadiaSettings settings{};
|
||||
settings._ParseJsonString(settingsJson, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_IS_FALSE(settings._profiles.empty(), 0);
|
||||
VERIFY_IS_FALSE(settings._profiles.empty());
|
||||
|
||||
GlobalAppSettings globalSettings{};
|
||||
auto terminalSettings = settings._profiles[0].CreateTerminalSettings(globalSettings.GetColorSchemes());
|
||||
@@ -2092,6 +2094,75 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::TestLayerProfileOnColorScheme()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Ensure that setting (or not) a property in the profile that should override a property of the color scheme works correctly."));
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"colorScheme": "schemeWithCursorColor"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"colorScheme": "schemeWithoutCursorColor"
|
||||
},
|
||||
{
|
||||
"name" : "profile2",
|
||||
"colorScheme": "schemeWithCursorColor",
|
||||
"cursorColor": "#234567"
|
||||
},
|
||||
{
|
||||
"name" : "profile3",
|
||||
"colorScheme": "schemeWithoutCursorColor",
|
||||
"cursorColor": "#345678"
|
||||
},
|
||||
{
|
||||
"name" : "profile4",
|
||||
"cursorColor": "#456789"
|
||||
},
|
||||
{
|
||||
"name" : "profile5"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{
|
||||
"name": "schemeWithCursorColor",
|
||||
"cursorColor": "#123456"
|
||||
},
|
||||
{
|
||||
"name": "schemeWithoutCursorColor"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settings0String);
|
||||
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings0String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(6u, settings._profiles.size());
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals._colorSchemes.size());
|
||||
|
||||
auto terminalSettings0 = settings._profiles[0].CreateTerminalSettings(settings._globals._colorSchemes);
|
||||
auto terminalSettings1 = settings._profiles[1].CreateTerminalSettings(settings._globals._colorSchemes);
|
||||
auto terminalSettings2 = settings._profiles[2].CreateTerminalSettings(settings._globals._colorSchemes);
|
||||
auto terminalSettings3 = settings._profiles[3].CreateTerminalSettings(settings._globals._colorSchemes);
|
||||
auto terminalSettings4 = settings._profiles[4].CreateTerminalSettings(settings._globals._colorSchemes);
|
||||
auto terminalSettings5 = settings._profiles[5].CreateTerminalSettings(settings._globals._colorSchemes);
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x12, 0x34, 0x56), terminalSettings0.CursorColor()); // from color scheme
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1.CursorColor()); // default
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x23, 0x45, 0x67), terminalSettings2.CursorColor()); // from profile (trumps color scheme)
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x34, 0x56, 0x78), terminalSettings3.CursorColor()); // from profile (not set in color scheme)
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x45, 0x67, 0x89), terminalSettings4.CursorColor()); // from profile (no color scheme)
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5.CursorColor()); // default
|
||||
}
|
||||
|
||||
void SettingsTests::ValidateKeybindingsWarnings()
|
||||
{
|
||||
const std::string badSettings{ R"(
|
||||
@@ -2134,5 +2205,4 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(2));
|
||||
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(3));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void**
|
||||
parentHwnd,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
nullptr);
|
||||
auto _terminal = std::make_unique<HwndTerminal>(_hostWindow);
|
||||
RETURN_IF_FAILED(_terminal->Initialize());
|
||||
|
||||
|
||||
@@ -516,7 +516,7 @@ std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementat
|
||||
warnings.insert(warnings.end(), parseWarnings.begin(), parseWarnings.end());
|
||||
|
||||
// if an arg parser was registered, but failed, bail
|
||||
if (args == nullptr)
|
||||
if (pfn && args == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace TerminalAppLocalTests
|
||||
namespace TerminalAppUnitTests
|
||||
{
|
||||
class DynamicProfileTests;
|
||||
class JsonTests;
|
||||
};
|
||||
|
||||
namespace TerminalApp
|
||||
@@ -124,4 +125,5 @@ private:
|
||||
friend class TerminalAppLocalTests::KeyBindingsTests;
|
||||
friend class TerminalAppLocalTests::TabTests;
|
||||
friend class TerminalAppUnitTests::DynamicProfileTests;
|
||||
friend class TerminalAppUnitTests::JsonTests;
|
||||
};
|
||||
|
||||
@@ -704,7 +704,7 @@ ColorScheme* CascadiaSettings::_FindMatchingColorScheme(const Json::Value& schem
|
||||
bool CascadiaSettings::_IsPackaged()
|
||||
{
|
||||
UINT32 length = 0;
|
||||
LONG rc = GetCurrentPackageFullName(&length, NULL);
|
||||
LONG rc = GetCurrentPackageFullName(&length, nullptr);
|
||||
return rc != APPMODEL_ERROR_NO_PACKAGE;
|
||||
}
|
||||
|
||||
@@ -724,15 +724,15 @@ void CascadiaSettings::_WriteSettings(const std::string_view content)
|
||||
wil::unique_hfile hOut{ CreateFileW(pathToSettingsFile.c_str(),
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
nullptr,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL) };
|
||||
nullptr) };
|
||||
if (!hOut)
|
||||
{
|
||||
THROW_LAST_ERROR();
|
||||
}
|
||||
THROW_LAST_ERROR_IF(!WriteFile(hOut.get(), content.data(), gsl::narrow<DWORD>(content.size()), 0, 0));
|
||||
THROW_LAST_ERROR_IF(!WriteFile(hOut.get(), content.data(), gsl::narrow<DWORD>(content.size()), nullptr, nullptr));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -853,7 +853,7 @@ std::wstring CascadiaSettings::GetSettingsPath(const bool useRoamingPath)
|
||||
// the new AppModel paths (Packages/xxx/RoamingState, etc.) for standard path requests.
|
||||
// Using this flag allows us to avoid Windows.Storage.ApplicationData completely.
|
||||
const auto knowFolderId = useRoamingPath ? FOLDERID_RoamingAppData : FOLDERID_LocalAppData;
|
||||
if (FAILED(SHGetKnownFolderPath(knowFolderId, KF_FLAG_FORCE_APP_DATA_REDIRECTION, 0, &localAppDataFolder)))
|
||||
if (FAILED(SHGetKnownFolderPath(knowFolderId, KF_FLAG_FORCE_APP_DATA_REDIRECTION, nullptr, &localAppDataFolder)))
|
||||
{
|
||||
THROW_LAST_ERROR();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ static constexpr std::string_view TableKey{ "colors" };
|
||||
static constexpr std::string_view ForegroundKey{ "foreground" };
|
||||
static constexpr std::string_view BackgroundKey{ "background" };
|
||||
static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" };
|
||||
static constexpr std::string_view CursorColorKey{ "cursorColor" };
|
||||
static constexpr std::array<std::string_view, 16> TableColors = {
|
||||
"black",
|
||||
"red",
|
||||
@@ -42,16 +43,18 @@ ColorScheme::ColorScheme() :
|
||||
_table{},
|
||||
_defaultForeground{ DEFAULT_FOREGROUND_WITH_ALPHA },
|
||||
_defaultBackground{ DEFAULT_BACKGROUND_WITH_ALPHA },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND }
|
||||
_selectionBackground{ DEFAULT_FOREGROUND },
|
||||
_cursorColor{ DEFAULT_CURSOR_COLOR }
|
||||
{
|
||||
}
|
||||
|
||||
ColorScheme::ColorScheme(std::wstring name, COLORREF defaultFg, COLORREF defaultBg) :
|
||||
ColorScheme::ColorScheme(std::wstring name, COLORREF defaultFg, COLORREF defaultBg, COLORREF cursorColor) :
|
||||
_schemeName{ name },
|
||||
_table{},
|
||||
_defaultForeground{ defaultFg },
|
||||
_defaultBackground{ defaultBg },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND }
|
||||
_selectionBackground{ DEFAULT_FOREGROUND },
|
||||
_cursorColor{ cursorColor }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -71,6 +74,7 @@ void ColorScheme::ApplyScheme(TerminalSettings terminalSettings) const
|
||||
terminalSettings.DefaultForeground(_defaultForeground);
|
||||
terminalSettings.DefaultBackground(_defaultBackground);
|
||||
terminalSettings.SelectionBackground(_selectionBackground);
|
||||
terminalSettings.CursorColor(_cursorColor);
|
||||
|
||||
auto const tableCount = gsl::narrow_cast<int>(_table.size());
|
||||
for (int i = 0; i < tableCount; i++)
|
||||
@@ -92,6 +96,7 @@ Json::Value ColorScheme::ToJson() const
|
||||
root[JsonKey(ForegroundKey)] = Utils::ColorToHexString(_defaultForeground);
|
||||
root[JsonKey(BackgroundKey)] = Utils::ColorToHexString(_defaultBackground);
|
||||
root[JsonKey(SelectionBackgroundKey)] = Utils::ColorToHexString(_selectionBackground);
|
||||
root[JsonKey(CursorColorKey)] = Utils::ColorToHexString(_cursorColor);
|
||||
|
||||
int i = 0;
|
||||
for (const auto& colorName : TableColors)
|
||||
@@ -166,6 +171,11 @@ void ColorScheme::LayerJson(const Json::Value& json)
|
||||
const auto color = Utils::ColorFromHexString(sbString.asString());
|
||||
_selectionBackground = color;
|
||||
}
|
||||
if (auto sbString{ json[JsonKey(CursorColorKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(sbString.asString());
|
||||
_cursorColor = color;
|
||||
}
|
||||
|
||||
// Legacy Deserialization. Leave in place to allow forward compatibility
|
||||
if (auto table{ json[JsonKey(TableKey)] })
|
||||
@@ -220,6 +230,11 @@ COLORREF ColorScheme::GetSelectionBackground() const noexcept
|
||||
return _selectionBackground;
|
||||
}
|
||||
|
||||
COLORREF ColorScheme::GetCursorColor() const noexcept
|
||||
{
|
||||
return _cursorColor;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Parse the name from the JSON representation of a ColorScheme.
|
||||
// Arguments:
|
||||
|
||||
@@ -35,7 +35,7 @@ class TerminalApp::ColorScheme
|
||||
{
|
||||
public:
|
||||
ColorScheme();
|
||||
ColorScheme(std::wstring name, COLORREF defaultFg, COLORREF defaultBg);
|
||||
ColorScheme(std::wstring name, COLORREF defaultFg, COLORREF defaultBg, COLORREF cursorColor);
|
||||
~ColorScheme();
|
||||
|
||||
void ApplyScheme(winrt::Microsoft::Terminal::Settings::TerminalSettings terminalSettings) const;
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
COLORREF GetForeground() const noexcept;
|
||||
COLORREF GetBackground() const noexcept;
|
||||
COLORREF GetSelectionBackground() const noexcept;
|
||||
COLORREF GetCursorColor() const noexcept;
|
||||
|
||||
static std::optional<std::wstring> GetNameFromJson(const Json::Value& json);
|
||||
|
||||
@@ -59,6 +60,7 @@ private:
|
||||
COLORREF _defaultForeground;
|
||||
COLORREF _defaultBackground;
|
||||
COLORREF _selectionBackground;
|
||||
COLORREF _cursorColor;
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
|
||||
@@ -262,22 +262,14 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
_defaultProfile = guid;
|
||||
}
|
||||
|
||||
if (auto alwaysShowTabs{ json[JsonKey(AlwaysShowTabsKey)] })
|
||||
{
|
||||
_alwaysShowTabs = alwaysShowTabs.asBool();
|
||||
}
|
||||
if (auto confirmCloseAllTabs{ json[JsonKey(ConfirmCloseAllKey)] })
|
||||
{
|
||||
_confirmCloseAllTabs = confirmCloseAllTabs.asBool();
|
||||
}
|
||||
if (auto initialRows{ json[JsonKey(InitialRowsKey)] })
|
||||
{
|
||||
_initialRows = initialRows.asInt();
|
||||
}
|
||||
if (auto initialCols{ json[JsonKey(InitialColsKey)] })
|
||||
{
|
||||
_initialCols = initialCols.asInt();
|
||||
}
|
||||
JsonUtils::GetBool(json, AlwaysShowTabsKey, _alwaysShowTabs);
|
||||
|
||||
JsonUtils::GetBool(json, ConfirmCloseAllKey, _confirmCloseAllTabs);
|
||||
|
||||
JsonUtils::GetInt(json, InitialRowsKey, _initialRows);
|
||||
|
||||
JsonUtils::GetInt(json, InitialColsKey, _initialCols);
|
||||
|
||||
if (auto rowsToScroll{ json[JsonKey(RowsToScrollKey)] })
|
||||
{
|
||||
//if it's not an int we fall back to setting it to 0, which implies using the system setting. This will be the case if it's set to "system"
|
||||
@@ -290,29 +282,19 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
_rowsToScroll = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto initialPosition{ json[JsonKey(InitialPositionKey)] })
|
||||
{
|
||||
_ParseInitialPosition(GetWstringFromJson(initialPosition), _initialX, _initialY);
|
||||
}
|
||||
if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] })
|
||||
{
|
||||
_showTitleInTitlebar = showTitleInTitlebar.asBool();
|
||||
}
|
||||
|
||||
if (auto showTabsInTitlebar{ json[JsonKey(ShowTabsInTitlebarKey)] })
|
||||
{
|
||||
_showTabsInTitlebar = showTabsInTitlebar.asBool();
|
||||
}
|
||||
JsonUtils::GetBool(json, ShowTitleInTitlebarKey, _showTitleInTitlebar);
|
||||
|
||||
if (auto wordDelimiters{ json[JsonKey(WordDelimitersKey)] })
|
||||
{
|
||||
_wordDelimiters = GetWstringFromJson(wordDelimiters);
|
||||
}
|
||||
JsonUtils::GetBool(json, ShowTabsInTitlebarKey, _showTabsInTitlebar);
|
||||
|
||||
if (auto copyOnSelect{ json[JsonKey(CopyOnSelectKey)] })
|
||||
{
|
||||
_copyOnSelect = copyOnSelect.asBool();
|
||||
}
|
||||
JsonUtils::GetWstring(json, WordDelimitersKey, _wordDelimiters);
|
||||
|
||||
JsonUtils::GetBool(json, CopyOnSelectKey, _copyOnSelect);
|
||||
|
||||
if (auto launchMode{ json[JsonKey(LaunchModeKey)] })
|
||||
{
|
||||
@@ -341,10 +323,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
_keybindingsWarnings.insert(_keybindingsWarnings.end(), warnings.begin(), warnings.end());
|
||||
}
|
||||
|
||||
if (auto snapToGridOnResize{ json[JsonKey(SnapToGridOnResizeKey)] })
|
||||
{
|
||||
_SnapToGridOnResize = snapToGridOnResize.asBool();
|
||||
}
|
||||
JsonUtils::GetBool(json, SnapToGridOnResizeKey, _SnapToGridOnResize);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -52,8 +52,74 @@ void TerminalApp::JsonUtils::GetOptionalDouble(const Json::Value& json,
|
||||
const auto conversionFn = [](const Json::Value& value) -> double {
|
||||
return value.asFloat();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isNumeric();
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn);
|
||||
conversionFn,
|
||||
validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
int& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> int {
|
||||
return value.asInt();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isInt();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetUInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
uint32_t& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> uint32_t {
|
||||
return value.asUInt();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isUInt();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
double& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> double {
|
||||
return value.asFloat();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isNumeric();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetBool(const Json::Value& json,
|
||||
std::string_view key,
|
||||
bool& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> bool {
|
||||
return value.asBool();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isBool();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetWstring(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::wstring& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> std::wstring {
|
||||
return GetWstringFromJson(value);
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, nullptr);
|
||||
}
|
||||
|
||||
@@ -46,24 +46,99 @@ namespace TerminalApp::JsonUtils
|
||||
// - target: the optional object to receive the value from json
|
||||
// - conversion: a std::function<T(const Json::Value&)> which can be used to
|
||||
// convert the Json::Value to the appropriate type.
|
||||
// - validation: optional, if provided, will be called first to ensure that
|
||||
// the json::value is of the correct type before attempting to call
|
||||
// `conversion`.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename T, typename F>
|
||||
void GetOptionalValue(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<T>& target,
|
||||
F&& conversion)
|
||||
F&& conversion,
|
||||
const std::function<bool(const Json::Value&)>& validation = nullptr)
|
||||
{
|
||||
if (json.isMember(JsonKey(key)))
|
||||
{
|
||||
if (auto jsonVal{ json[JsonKey(key)] })
|
||||
{
|
||||
target = conversion(jsonVal);
|
||||
if (validation == nullptr || validation(jsonVal))
|
||||
{
|
||||
target = conversion(jsonVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This branch is hit when the json object contained the key,
|
||||
// but the key was set to `null`. In this case, explicitly clear
|
||||
// the target.
|
||||
target = std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper that can be used for retrieving a value from a json
|
||||
// object, and parsing it's value to set on a given target object.
|
||||
// - If the key we're looking for _doesn't_ exist in the json object,
|
||||
// we'll leave the target object unmodified.
|
||||
// - If the key exists in the json object, we'll use the provided
|
||||
// `validation` function to ensure that the json value is of the
|
||||
// correct type.
|
||||
// - If we successfully validate the json value type (or no validation
|
||||
// function was provided), then we'll use `conversion` to parse the
|
||||
// value and place the result into `target`
|
||||
// - Each caller should provide a conversion function that takes a
|
||||
// Json::Value and returns an object of the same type as target.
|
||||
// - Unlike GetOptionalValue, if the key exists but is set to `null`, we'll
|
||||
// just ignore it.
|
||||
// Arguments:
|
||||
// - json: The json object to search for the given key
|
||||
// - key: The key to look for in the json object
|
||||
// - target: the optional object to receive the value from json
|
||||
// - conversion: a std::function<T(const Json::Value&)> which can be used to
|
||||
// convert the Json::Value to the appropriate type.
|
||||
// - validation: optional, if provided, will be called first to ensure that
|
||||
// the json::value is of the correct type before attempting to call
|
||||
// `conversion`.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename T, typename F>
|
||||
void GetValue(const Json::Value& json,
|
||||
std::string_view key,
|
||||
T& target,
|
||||
F&& conversion,
|
||||
const std::function<bool(const Json::Value&)>& validation = nullptr)
|
||||
{
|
||||
if (json.isMember(JsonKey(key)))
|
||||
{
|
||||
if (auto jsonVal{ json[JsonKey(key)] })
|
||||
{
|
||||
if (validation == nullptr || validation(jsonVal))
|
||||
{
|
||||
target = conversion(jsonVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
int& target);
|
||||
|
||||
void GetUInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
uint32_t& target);
|
||||
|
||||
void GetDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
double& target);
|
||||
|
||||
void GetBool(const Json::Value& json,
|
||||
std::string_view key,
|
||||
bool& target);
|
||||
|
||||
void GetWstring(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::wstring& target);
|
||||
};
|
||||
|
||||
@@ -803,10 +803,10 @@ void Pane::_CreateRowColDefinitions(const Size& rootSize)
|
||||
const auto paneSizes = _CalcChildrenSizes(rootSize.Width);
|
||||
|
||||
auto firstColDef = Controls::ColumnDefinition();
|
||||
firstColDef.Width(GridLengthHelper::FromPixels(paneSizes.first));
|
||||
firstColDef.Width(GridLengthHelper::FromValueAndType(paneSizes.first, GridUnitType::Star));
|
||||
|
||||
auto secondColDef = Controls::ColumnDefinition();
|
||||
secondColDef.Width(GridLengthHelper::FromPixels(paneSizes.second));
|
||||
secondColDef.Width(GridLengthHelper::FromValueAndType(paneSizes.second, GridUnitType::Star));
|
||||
|
||||
_root.ColumnDefinitions().Append(firstColDef);
|
||||
_root.ColumnDefinitions().Append(secondColDef);
|
||||
@@ -819,10 +819,10 @@ void Pane::_CreateRowColDefinitions(const Size& rootSize)
|
||||
const auto paneSizes = _CalcChildrenSizes(rootSize.Height);
|
||||
|
||||
auto firstRowDef = Controls::RowDefinition();
|
||||
firstRowDef.Height(GridLengthHelper::FromPixels(paneSizes.first));
|
||||
firstRowDef.Height(GridLengthHelper::FromValueAndType(paneSizes.first, GridUnitType::Star));
|
||||
|
||||
auto secondRowDef = Controls::RowDefinition();
|
||||
secondRowDef.Height(GridLengthHelper::FromPixels(paneSizes.second));
|
||||
secondRowDef.Height(GridLengthHelper::FromValueAndType(paneSizes.second, GridUnitType::Star));
|
||||
|
||||
_root.RowDefinitions().Append(firstRowDef);
|
||||
_root.RowDefinitions().Append(secondRowDef);
|
||||
|
||||
@@ -196,7 +196,7 @@ catch (...)
|
||||
static void _accumulateStorePowerShellInstances(std::vector<PowerShellInstance>& out)
|
||||
{
|
||||
wil::unique_cotaskmem_string localAppDataFolder;
|
||||
if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, 0, &localAppDataFolder)))
|
||||
if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localAppDataFolder)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,12 +104,12 @@ Profile::Profile(const std::optional<GUID>& guid) :
|
||||
_defaultForeground{},
|
||||
_defaultBackground{},
|
||||
_selectionBackground{},
|
||||
_cursorColor{},
|
||||
_colorTable{},
|
||||
_tabTitle{},
|
||||
_suppressApplicationTitle{},
|
||||
_historySize{ DEFAULT_HISTORY_SIZE },
|
||||
_snapOnInput{ true },
|
||||
_cursorColor{ DEFAULT_CURSOR_COLOR },
|
||||
_cursorShape{ CursorStyle::Bar },
|
||||
_cursorHeight{ DEFAULT_CURSOR_HEIGHT },
|
||||
|
||||
@@ -178,7 +178,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
}
|
||||
terminalSettings.HistorySize(_historySize);
|
||||
terminalSettings.SnapOnInput(_snapOnInput);
|
||||
terminalSettings.CursorColor(_cursorColor);
|
||||
terminalSettings.CursorHeight(_cursorHeight);
|
||||
terminalSettings.CursorShape(_cursorShape);
|
||||
|
||||
@@ -228,6 +227,10 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
{
|
||||
terminalSettings.SelectionBackground(_selectionBackground.value());
|
||||
}
|
||||
if (_cursorColor)
|
||||
{
|
||||
terminalSettings.CursorColor(_cursorColor.value());
|
||||
}
|
||||
|
||||
if (_scrollbarState)
|
||||
{
|
||||
@@ -296,6 +299,10 @@ Json::Value Profile::ToJson() const
|
||||
{
|
||||
root[JsonKey(SelectionBackgroundKey)] = Utils::ColorToHexString(_selectionBackground.value());
|
||||
}
|
||||
if (_cursorColor)
|
||||
{
|
||||
root[JsonKey(CursorColorKey)] = Utils::ColorToHexString(_cursorColor.value());
|
||||
}
|
||||
if (_schemeName)
|
||||
{
|
||||
const auto scheme = winrt::to_string(_schemeName.value());
|
||||
@@ -312,7 +319,6 @@ Json::Value Profile::ToJson() const
|
||||
}
|
||||
root[JsonKey(HistorySizeKey)] = _historySize;
|
||||
root[JsonKey(SnapOnInputKey)] = _snapOnInput;
|
||||
root[JsonKey(CursorColorKey)] = Utils::ColorToHexString(_cursorColor);
|
||||
// Only add the cursor height property if we're a legacy-style cursor.
|
||||
if (_cursorShape == CursorStyle::Vintage)
|
||||
{
|
||||
@@ -622,19 +628,11 @@ bool Profile::_ConvertJsonToBool(const Json::Value& json)
|
||||
void Profile::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// Profile-specific Settings
|
||||
if (json.isMember(JsonKey(NameKey)))
|
||||
{
|
||||
auto name{ json[JsonKey(NameKey)] };
|
||||
_name = GetWstringFromJson(name);
|
||||
}
|
||||
JsonUtils::GetWstring(json, NameKey, _name);
|
||||
|
||||
JsonUtils::GetOptionalGuid(json, GuidKey, _guid);
|
||||
|
||||
if (json.isMember(JsonKey(HiddenKey)))
|
||||
{
|
||||
auto hidden{ json[JsonKey(HiddenKey)] };
|
||||
_hidden = hidden.asBool();
|
||||
}
|
||||
JsonUtils::GetBool(json, HiddenKey, _hidden);
|
||||
|
||||
// Core Settings
|
||||
JsonUtils::GetOptionalColor(json, ForegroundKey, _defaultForeground);
|
||||
@@ -643,6 +641,8 @@ void Profile::LayerJson(const Json::Value& json)
|
||||
|
||||
JsonUtils::GetOptionalColor(json, SelectionBackgroundKey, _selectionBackground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, CursorColorKey, _cursorColor);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKey, _schemeName);
|
||||
// TODO:GH#1069 deprecate old settings key
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKeyOld, _schemeName);
|
||||
@@ -664,28 +664,14 @@ void Profile::LayerJson(const Json::Value& json)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (json.isMember(JsonKey(HistorySizeKey)))
|
||||
{
|
||||
auto historySize{ json[JsonKey(HistorySizeKey)] };
|
||||
// TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback"
|
||||
_historySize = historySize.asInt();
|
||||
}
|
||||
if (json.isMember(JsonKey(SnapOnInputKey)))
|
||||
{
|
||||
auto snapOnInput{ json[JsonKey(SnapOnInputKey)] };
|
||||
_snapOnInput = snapOnInput.asBool();
|
||||
}
|
||||
if (json.isMember(JsonKey(CursorColorKey)))
|
||||
{
|
||||
auto cursorColor{ json[JsonKey(CursorColorKey)] };
|
||||
const auto color = Utils::ColorFromHexString(cursorColor.asString());
|
||||
_cursorColor = color;
|
||||
}
|
||||
if (json.isMember(JsonKey(CursorHeightKey)))
|
||||
{
|
||||
auto cursorHeight{ json[JsonKey(CursorHeightKey)] };
|
||||
_cursorHeight = cursorHeight.asUInt();
|
||||
}
|
||||
|
||||
// TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback"
|
||||
JsonUtils::GetInt(json, HistorySizeKey, _historySize);
|
||||
|
||||
JsonUtils::GetBool(json, SnapOnInputKey, _snapOnInput);
|
||||
|
||||
JsonUtils::GetUInt(json, CursorHeightKey, _cursorHeight);
|
||||
|
||||
if (json.isMember(JsonKey(CursorShapeKey)))
|
||||
{
|
||||
auto cursorShape{ json[JsonKey(CursorShapeKey)] };
|
||||
@@ -696,46 +682,25 @@ void Profile::LayerJson(const Json::Value& json)
|
||||
// Control Settings
|
||||
JsonUtils::GetOptionalGuid(json, ConnectionTypeKey, _connectionType);
|
||||
|
||||
if (json.isMember(JsonKey(CommandlineKey)))
|
||||
{
|
||||
auto commandline{ json[JsonKey(CommandlineKey)] };
|
||||
_commandline = GetWstringFromJson(commandline);
|
||||
}
|
||||
if (json.isMember(JsonKey(FontFaceKey)))
|
||||
{
|
||||
auto fontFace{ json[JsonKey(FontFaceKey)] };
|
||||
_fontFace = GetWstringFromJson(fontFace);
|
||||
}
|
||||
if (json.isMember(JsonKey(FontSizeKey)))
|
||||
{
|
||||
auto fontSize{ json[JsonKey(FontSizeKey)] };
|
||||
_fontSize = fontSize.asInt();
|
||||
}
|
||||
if (json.isMember(JsonKey(AcrylicTransparencyKey)))
|
||||
{
|
||||
auto acrylicTransparency{ json[JsonKey(AcrylicTransparencyKey)] };
|
||||
_acrylicTransparency = acrylicTransparency.asFloat();
|
||||
}
|
||||
if (json.isMember(JsonKey(UseAcrylicKey)))
|
||||
{
|
||||
auto useAcrylic{ json[JsonKey(UseAcrylicKey)] };
|
||||
_useAcrylic = useAcrylic.asBool();
|
||||
}
|
||||
if (json.isMember(JsonKey(SuppressApplicationTitleKey)))
|
||||
{
|
||||
auto suppressApplicationTitle{ json[JsonKey(SuppressApplicationTitleKey)] };
|
||||
_suppressApplicationTitle = suppressApplicationTitle.asBool();
|
||||
}
|
||||
JsonUtils::GetWstring(json, CommandlineKey, _commandline);
|
||||
|
||||
JsonUtils::GetWstring(json, FontFaceKey, _fontFace);
|
||||
|
||||
JsonUtils::GetInt(json, FontSizeKey, _fontSize);
|
||||
|
||||
JsonUtils::GetDouble(json, AcrylicTransparencyKey, _acrylicTransparency);
|
||||
|
||||
JsonUtils::GetBool(json, UseAcrylicKey, _useAcrylic);
|
||||
|
||||
JsonUtils::GetBool(json, SuppressApplicationTitleKey, _suppressApplicationTitle);
|
||||
|
||||
if (json.isMember(JsonKey(CloseOnExitKey)))
|
||||
{
|
||||
auto closeOnExit{ json[JsonKey(CloseOnExitKey)] };
|
||||
_closeOnExitMode = ParseCloseOnExitMode(closeOnExit);
|
||||
}
|
||||
if (json.isMember(JsonKey(PaddingKey)))
|
||||
{
|
||||
auto padding{ json[JsonKey(PaddingKey)] };
|
||||
_padding = GetWstringFromJson(padding);
|
||||
}
|
||||
|
||||
JsonUtils::GetWstring(json, PaddingKey, _padding);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ScrollbarStateKey, _scrollbarState);
|
||||
|
||||
|
||||
@@ -142,12 +142,12 @@ private:
|
||||
std::optional<uint32_t> _defaultForeground;
|
||||
std::optional<uint32_t> _defaultBackground;
|
||||
std::optional<uint32_t> _selectionBackground;
|
||||
std::optional<uint32_t> _cursorColor;
|
||||
std::array<uint32_t, COLOR_TABLE_SIZE> _colorTable;
|
||||
std::optional<std::wstring> _tabTitle;
|
||||
bool _suppressApplicationTitle;
|
||||
int32_t _historySize;
|
||||
bool _snapOnInput;
|
||||
uint32_t _cursorColor;
|
||||
uint32_t _cursorHeight;
|
||||
winrt::Microsoft::Terminal::Settings::CursorStyle _cursorShape;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||||
<!-- ========================= XAML files ======================== -->
|
||||
<ItemGroup>
|
||||
<!-- DON'T PUT XAML FILES HERE! Put them in TerminalAppLib.vcxproj -->
|
||||
@@ -77,11 +78,17 @@
|
||||
<CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- A small helper for paths to the compiled cppwinrt projects -->
|
||||
<_BinRoot Condition="'$(Platform)' != 'Win32'">$(OpenConsoleDir)$(Platform)\$(Configuration)\</_BinRoot>
|
||||
<_BinRoot Condition="'$(Platform)' == 'Win32'">$(OpenConsoleDir)$(Configuration)\</_BinRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.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.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
</Target>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
|
||||
@@ -673,8 +673,9 @@ namespace winrt::TerminalApp::implementation
|
||||
const RoutedEventArgs&)
|
||||
{
|
||||
const auto feedbackUriValue = RS_(L"FeedbackUriValue");
|
||||
winrt::Windows::Foundation::Uri feedbackUri{ feedbackUriValue };
|
||||
|
||||
winrt::Windows::System::Launcher::LaunchUriAsync({ feedbackUriValue });
|
||||
winrt::Windows::System::Launcher::LaunchUriAsync(feedbackUri);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -63,13 +63,13 @@ std::vector<TerminalApp::Profile> WslDistroGenerator::GenerateProfiles()
|
||||
nullptr,
|
||||
&si,
|
||||
&pi));
|
||||
switch (WaitForSingleObject(pi.hProcess, INFINITE))
|
||||
switch (WaitForSingleObject(pi.hProcess, 2000))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_ABANDONED:
|
||||
case WAIT_TIMEOUT:
|
||||
THROW_HR(ERROR_CHILD_NOT_COMPLETE);
|
||||
return profiles;
|
||||
case WAIT_FAILED:
|
||||
THROW_LAST_ERROR();
|
||||
default:
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
"startingDirectory": "%USERPROFILE%",
|
||||
"closeOnExit": "graceful",
|
||||
"colorScheme": "Campbell Powershell",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"cursorShape": "bar",
|
||||
"fontFace": "Consolas",
|
||||
"fontSize": 12,
|
||||
@@ -41,7 +40,6 @@
|
||||
"startingDirectory": "%USERPROFILE%",
|
||||
"closeOnExit": "graceful",
|
||||
"colorScheme": "Campbell",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"cursorShape": "bar",
|
||||
"fontFace": "Consolas",
|
||||
"fontSize": 12,
|
||||
@@ -59,6 +57,7 @@
|
||||
"name": "Campbell",
|
||||
"foreground": "#CCCCCC",
|
||||
"background": "#0C0C0C",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#0C0C0C",
|
||||
"red": "#C50F1F",
|
||||
"green": "#13A10E",
|
||||
@@ -80,6 +79,7 @@
|
||||
"name": "Campbell Powershell",
|
||||
"foreground": "#CCCCCC",
|
||||
"background": "#012456",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#0C0C0C",
|
||||
"red": "#C50F1F",
|
||||
"green": "#13A10E",
|
||||
@@ -101,6 +101,7 @@
|
||||
"name": "Vintage",
|
||||
"foreground": "#C0C0C0",
|
||||
"background": "#000000",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#000000",
|
||||
"red": "#800000",
|
||||
"green": "#008000",
|
||||
@@ -122,6 +123,7 @@
|
||||
"name": "One Half Dark",
|
||||
"foreground": "#DCDFE4",
|
||||
"background": "#282C34",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#282C34",
|
||||
"red": "#E06C75",
|
||||
"green": "#98C379",
|
||||
@@ -143,6 +145,7 @@
|
||||
"name": "One Half Light",
|
||||
"foreground": "#383A42",
|
||||
"background": "#FAFAFA",
|
||||
"cursorColor": "#4F525D",
|
||||
"black": "#383A42",
|
||||
"red": "#E45649",
|
||||
"green": "#50A14F",
|
||||
@@ -164,6 +167,7 @@
|
||||
"name": "Solarized Dark",
|
||||
"foreground": "#839496",
|
||||
"background": "#002B36",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#073642",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
@@ -185,6 +189,7 @@
|
||||
"name": "Solarized Light",
|
||||
"foreground": "#657B83",
|
||||
"background": "#FDF6E3",
|
||||
"cursorColor": "#002B36",
|
||||
"black": "#073642",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
|
||||
@@ -22,9 +22,11 @@
|
||||
When we do this, the XBF ends up in resources.pri.
|
||||
-->
|
||||
<DisableEmbeddedXbf>false</DisableEmbeddedXbf>
|
||||
<XamlComponentResourceLocation>nested</XamlComponentResourceLocation>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
@@ -282,39 +284,9 @@
|
||||
</ItemDefinitionGroup>
|
||||
<!-- ========================= Globals ======================== -->
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
<!-- Manually include MUX here, instead of importing its targets file. We need
|
||||
to reference its winmd, but very specifically with the
|
||||
CopyLocalSatelliteAssemblies and Private properties set to false, as to not
|
||||
have projects including us double-including MUX's .winmd. The following blob
|
||||
is taken straight from the MUX build targets, with the aforementioned
|
||||
changes.-->
|
||||
<PropertyGroup>
|
||||
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
|
||||
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
|
||||
<_MUXRoot>$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\</_MUXRoot>
|
||||
<_MUXAppRoot>$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\</_MUXAppRoot>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Microsoft.UI.XAML -->
|
||||
<Reference Include="$(_MUXRoot)lib\uap10.0\Microsoft.UI.Xaml.winmd">
|
||||
<Implementation>Microsoft.UI.Xaml.dll</Implementation>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
<Private>true</Private>
|
||||
</Reference>
|
||||
<ReferenceCopyLocalPaths Include="$(_MUXRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.UI.Xaml.dll" />
|
||||
<ReferenceCopyLocalPaths Include="$(_MUXRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.UI.Xaml.pri" />
|
||||
<!-- Microsoft.UI.XAML.Application -->
|
||||
<Reference Include="$(_MUXAppRoot)lib\uap10.0\Microsoft.Toolkit.Win32.UI.XamlHost.winmd">
|
||||
<Implementation>Microsoft.Toolkit.Win32.UI.XamlHost.dll</Implementation>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<ReferenceCopyLocalPaths Include="$(_MUXAppRoot)lib\uap10.0\Microsoft.Toolkit.Win32.UI.XamlHost.*" />
|
||||
<ReferenceCopyLocalPaths Include="$(_MUXAppRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.Toolkit.Win32.UI.XamlHost.*" />
|
||||
</ItemGroup>
|
||||
<!-- End MUX import -->
|
||||
|
||||
<Import Project="..\..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.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>
|
||||
@@ -322,6 +294,7 @@
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.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.
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.0.0" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.3.191217003-prerelease" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
<package id="vcpkg-cpprestsdk" version="2.10.14" targetFramework="native" />
|
||||
<package id="vcpkg-telnetpp" version="1.0.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -36,6 +36,7 @@
|
||||
<Style x:Key="SearchBoxBackground" TargetType="StackPanel">
|
||||
<Setter Property="Background" Value="#333333" />
|
||||
</Style>
|
||||
<Style x:Key="PathStyle" TargetType="PathIcon" />
|
||||
<!-- TextBox colors !-->
|
||||
<SolidColorBrush x:Key="TextControlBackground" Color="#333333"/>
|
||||
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush" Color="#B5B5B5"/>
|
||||
@@ -93,6 +94,7 @@
|
||||
<Style x:Key="SearchBoxBackground" TargetType="StackPanel">
|
||||
<Setter Property="Background" Value="#CCCCCC" />
|
||||
</Style>
|
||||
<Style x:Key="PathStyle" TargetType="PathIcon" />
|
||||
<!-- TextBox colors !-->
|
||||
<SolidColorBrush x:Key="TextControlBackground" Color="#CCCCCC"/>
|
||||
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush" Color="#636363"/>
|
||||
@@ -147,6 +149,9 @@
|
||||
<Style x:Key="SearchBoxBackground" TargetType="StackPanel">
|
||||
<Setter Property="Background" Value="{ThemeResource SystemColorWindowColor}" />
|
||||
</Style>
|
||||
<Style x:Key="PathStyle" TargetType="PathIcon">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemColorWindowTextColor}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
@@ -183,7 +188,8 @@
|
||||
<ToggleButton x:Name="CaseSensitivityButton"
|
||||
x:Uid="SearchBox_CaseSensitivity"
|
||||
Style="{StaticResource ToggleButtonStyle}">
|
||||
<PathIcon Data="M8.87305 10H7.60156L6.5625 7.25195H2.40625L1.42871 10H0.150391L3.91016 0.197266H5.09961L8.87305 10ZM6.18652 6.21973L4.64844 2.04297C4.59831 1.90625 4.54818 1.6875 4.49805 1.38672H4.4707C4.42513 1.66471 4.37272 1.88346 4.31348 2.04297L2.78906 6.21973H6.18652ZM15.1826 10H14.0615V8.90625H14.0342C13.5465 9.74479 12.8288 10.1641 11.8809 10.1641C11.1836 10.1641 10.6367 9.97949 10.2402 9.61035C9.84831 9.24121 9.65234 8.7513 9.65234 8.14062C9.65234 6.83268 10.4225 6.07161 11.9629 5.85742L14.0615 5.56348C14.0615 4.37402 13.5807 3.7793 12.6191 3.7793C11.776 3.7793 11.015 4.06641 10.3359 4.64062V3.49219C11.0241 3.05469 11.8171 2.83594 12.7148 2.83594C14.36 2.83594 15.1826 3.70638 15.1826 5.44727V10ZM14.0615 6.45898L12.373 6.69141C11.8535 6.76432 11.4616 6.89421 11.1973 7.08105C10.9329 7.26335 10.8008 7.58919 10.8008 8.05859C10.8008 8.40039 10.9215 8.68066 11.1631 8.89941C11.4092 9.11361 11.735 9.2207 12.1406 9.2207C12.6966 9.2207 13.1546 9.02702 13.5146 8.63965C13.8792 8.24772 14.0615 7.75326 14.0615 7.15625V6.45898Z"/>
|
||||
<PathIcon Data="M8.87305 10H7.60156L6.5625 7.25195H2.40625L1.42871 10H0.150391L3.91016 0.197266H5.09961L8.87305 10ZM6.18652 6.21973L4.64844 2.04297C4.59831 1.90625 4.54818 1.6875 4.49805 1.38672H4.4707C4.42513 1.66471 4.37272 1.88346 4.31348 2.04297L2.78906 6.21973H6.18652ZM15.1826 10H14.0615V8.90625H14.0342C13.5465 9.74479 12.8288 10.1641 11.8809 10.1641C11.1836 10.1641 10.6367 9.97949 10.2402 9.61035C9.84831 9.24121 9.65234 8.7513 9.65234 8.14062C9.65234 6.83268 10.4225 6.07161 11.9629 5.85742L14.0615 5.56348C14.0615 4.37402 13.5807 3.7793 12.6191 3.7793C11.776 3.7793 11.015 4.06641 10.3359 4.64062V3.49219C11.0241 3.05469 11.8171 2.83594 12.7148 2.83594C14.36 2.83594 15.1826 3.70638 15.1826 5.44727V10ZM14.0615 6.45898L12.373 6.69141C11.8535 6.76432 11.4616 6.89421 11.1973 7.08105C10.9329 7.26335 10.8008 7.58919 10.8008 8.05859C10.8008 8.40039 10.9215 8.68066 11.1631 8.89941C11.4092 9.11361 11.735 9.2207 12.1406 9.2207C12.6966 9.2207 13.1546 9.02702 13.5146 8.63965C13.8792 8.24772 14.0615 7.75326 14.0615 7.15625V6.45898Z"
|
||||
Style="{ThemeResource PathStyle}" />
|
||||
</ToggleButton>
|
||||
|
||||
<Button x:Name="CloseButton"
|
||||
|
||||
@@ -163,24 +163,27 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// Get scale factor for view
|
||||
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
|
||||
// Set the selection layout bounds
|
||||
Rect selectionRect = Rect(screenCursorPos.X, screenCursorPos.Y, 0, fontHeight);
|
||||
// position textblock to cursor position
|
||||
Canvas().SetLeft(TextBlock(), clientCursorPos.X);
|
||||
Canvas().SetTop(TextBlock(), ::base::ClampedNumeric<double>(clientCursorPos.Y));
|
||||
|
||||
// calculate FontSize in pixels from DIPs
|
||||
const double fontSizePx = (fontHeight * 72) / USER_DEFAULT_SCREEN_DPI;
|
||||
TextBlock().FontSize(fontSizePx);
|
||||
TextBlock().FontFamily(Media::FontFamily(fontArgs->FontFace()));
|
||||
|
||||
const auto widthToTerminalEnd = Canvas().ActualWidth() - ::base::ClampedNumeric<double>(clientCursorPos.X);
|
||||
TextBlock().MaxWidth(widthToTerminalEnd);
|
||||
|
||||
// Set the text block bounds
|
||||
const auto yOffset = ::base::ClampedNumeric<float>(TextBlock().ActualHeight()) - fontHeight;
|
||||
const auto textBottom = ::base::ClampedNumeric<float>(screenCursorPos.Y) + yOffset;
|
||||
Rect selectionRect = Rect(screenCursorPos.X, textBottom, 0, fontHeight);
|
||||
request.LayoutBounds().TextBounds(ScaleRect(selectionRect, scaleFactor));
|
||||
|
||||
// Set the control bounds of the whole control
|
||||
Rect controlRect = Rect(screenCursorPos.X, screenCursorPos.Y, 0, fontHeight);
|
||||
request.LayoutBounds().ControlBounds(ScaleRect(controlRect, scaleFactor));
|
||||
|
||||
// position textblock to cursor position
|
||||
Canvas().SetLeft(TextBlock(), clientCursorPos.X);
|
||||
Canvas().SetTop(TextBlock(), ::base::ClampedNumeric<double>(clientCursorPos.Y));
|
||||
|
||||
TextBlock().Height(fontHeight);
|
||||
// calculate FontSize in pixels from DIPs
|
||||
const double fontSizePx = (fontHeight * 72) / USER_DEFAULT_SCREEN_DPI;
|
||||
TextBlock().FontSize(fontSizePx);
|
||||
|
||||
TextBlock().FontFamily(Media::FontFamily(fontArgs->FontFace()));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -297,6 +300,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
try
|
||||
{
|
||||
// When a user deletes the last character in their current composition, some machines
|
||||
// will fire a CompositionCompleted before firing a TextUpdating event that deletes the last character.
|
||||
// The TextUpdating will have a lower StartCaretPosition, so in this scenario, _activeTextStart
|
||||
// needs to update to be the StartCaretPosition.
|
||||
// A known issue related to this behavior is that the last character that's deleted from a composition
|
||||
// will get sent to the terminal before we receive the TextUpdate to delete the character.
|
||||
// See GH #5054.
|
||||
_activeTextStart = ::base::ClampMin(_activeTextStart, ::base::ClampedNumeric<size_t>(range.StartCaretPosition));
|
||||
|
||||
_inputBuffer = _inputBuffer.replace(
|
||||
range.StartCaretPosition,
|
||||
::base::ClampSub<size_t>(range.EndCaretPosition, range.StartCaretPosition),
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
Visibility="Collapsed">
|
||||
<TextBlock x:Name="TextBlock"
|
||||
IsTextSelectionEnabled="false"
|
||||
TextWrapping="Wrap"
|
||||
TextDecorations="Underline" />
|
||||
</Canvas>
|
||||
</UserControl>
|
||||
|
||||
@@ -724,6 +724,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
bool handled = false;
|
||||
|
||||
// Alt-Numpad# input will send us a character once the user releases Alt, so we should be ignoring the individual keydowns.
|
||||
// The character will be sent through the TSFInputControl.
|
||||
// See GH#1401 for more details
|
||||
if (modifiers.IsAltPressed() && (e.OriginalKey() >= VirtualKey::NumberPad0 && e.OriginalKey() <= VirtualKey::NumberPad9))
|
||||
|
||||
{
|
||||
e.Handled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// GH#2235: Terminal::Settings hasn't been modified to differentiate between AltGr and Ctrl+Alt yet.
|
||||
// -> Don't check for key bindings if this is an AltGr key combination.
|
||||
if (!modifiers.IsAltGrPressed())
|
||||
@@ -1965,8 +1975,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
double height = rows * fFontHeight;
|
||||
auto thickness = _ParseThicknessFromPadding(settings.Padding());
|
||||
width += thickness.Left + thickness.Right;
|
||||
height += thickness.Top + thickness.Bottom;
|
||||
// GH#2061 - make sure to account for the size the padding _will be_ scaled to
|
||||
width += scale * (thickness.Left + thickness.Right);
|
||||
height += scale * (thickness.Top + thickness.Bottom);
|
||||
|
||||
return { gsl::narrow_cast<float>(width), gsl::narrow_cast<float>(height) };
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "TermControlAutomationPeer.g.cpp"
|
||||
|
||||
#include "XamlUiaTextRange.h"
|
||||
#include "..\types\UiaTracing.h"
|
||||
|
||||
using namespace Microsoft::Console::Types;
|
||||
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
||||
@@ -44,6 +45,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - <none>
|
||||
void TermControlAutomationPeer::SignalSelectionChanged()
|
||||
{
|
||||
UiaTracing::Signal::SelectionChanged();
|
||||
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
|
||||
// The event that is raised when the text selection is modified.
|
||||
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
|
||||
@@ -58,6 +60,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - <none>
|
||||
void TermControlAutomationPeer::SignalTextChanged()
|
||||
{
|
||||
UiaTracing::Signal::TextChanged();
|
||||
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
|
||||
// The event that is raised when textual content is modified.
|
||||
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextChanged);
|
||||
@@ -72,9 +75,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - <none>
|
||||
void TermControlAutomationPeer::SignalCursorChanged()
|
||||
{
|
||||
UiaTracing::Signal::CursorChanged();
|
||||
Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [&]() {
|
||||
// The event that is raised when the text was changed in an edit control.
|
||||
RaiseAutomationEvent(AutomationEvents::TextEditTextChanged);
|
||||
// Do NOT fire a TextEditTextChanged. Generally, an app on the other side
|
||||
// will expect more information. Though you can dispatch that event
|
||||
// on its own, it may result in a nullptr exception on the other side
|
||||
// because no additional information was provided. Crashing the screen
|
||||
// reader.
|
||||
RaiseAutomationEvent(AutomationEvents::TextPatternOnTextSelectionChanged);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
the default OutDir up one level, so the wapproj will be able to find it.
|
||||
-->
|
||||
<NoOutputRedirection>true</NoOutputRedirection>
|
||||
<XamlComponentResourceLocation>nested</XamlComponentResourceLocation>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -173,15 +173,17 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
const auto dx = viewportSize.X - oldDimensions.X;
|
||||
|
||||
const auto dx = ::base::ClampSub(viewportSize.X, oldDimensions.X);
|
||||
|
||||
const auto oldTop = _mutableViewport.Top();
|
||||
|
||||
const short newBufferHeight = viewportSize.Y + _scrollbackLines;
|
||||
const short newBufferHeight = ::base::ClampAdd(viewportSize.Y, _scrollbackLines);
|
||||
|
||||
COORD bufferSize{ viewportSize.X, newBufferHeight };
|
||||
|
||||
// Save cursor's relative height versus the viewport
|
||||
const short sCursorHeightInViewportBefore = _buffer->GetCursor().GetPosition().Y - _mutableViewport.Top();
|
||||
const short sCursorHeightInViewportBefore = ::base::ClampSub(_buffer->GetCursor().GetPosition().Y, _mutableViewport.Top());
|
||||
|
||||
// This will be used to determine where the viewport should be in the new buffer.
|
||||
const short oldViewportTop = _mutableViewport.Top();
|
||||
@@ -266,7 +268,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
|
||||
const auto maxRow = std::max(newLastChar.Y, newCursorPos.Y);
|
||||
|
||||
const short proposedTopFromLastLine = ::base::saturated_cast<short>(maxRow - viewportSize.Y + 1);
|
||||
const short proposedTopFromLastLine = ::base::ClampAdd(::base::ClampSub(maxRow, viewportSize.Y), 1);
|
||||
const short proposedTopFromScrollback = newViewportTop;
|
||||
|
||||
short proposedTop = std::max(proposedTopFromLastLine,
|
||||
@@ -294,7 +296,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
{
|
||||
try
|
||||
{
|
||||
auto row = newTextBuffer->GetRowByOffset(::base::saturated_cast<short>(proposedTop - 1));
|
||||
auto row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1));
|
||||
if (row.GetCharRow().WasWrapForced())
|
||||
{
|
||||
proposedTop--;
|
||||
@@ -324,7 +326,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
const auto proposedBottom = newView.BottomExclusive();
|
||||
if (proposedBottom > bufferSize.Y)
|
||||
{
|
||||
proposedTop = ::base::saturated_cast<short>(proposedTop - (proposedBottom - bufferSize.Y));
|
||||
proposedTop = ::base::ClampSub(proposedTop, ::base::ClampSub(proposedBottom, bufferSize.Y));
|
||||
}
|
||||
|
||||
_mutableViewport = Viewport::FromDimensions({ 0, proposedTop }, viewportSize);
|
||||
@@ -339,7 +341,14 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting
|
||||
|
||||
// If the old scrolloffset was 0, then we weren't scrolled back at all
|
||||
// before, and shouldn't be now either.
|
||||
_scrollOffset = originalOffsetWasZero ? 0 : _mutableViewport.Top() - newVisibleTop;
|
||||
_scrollOffset = originalOffsetWasZero ? 0 : ::base::ClampSub(_mutableViewport.Top(), newVisibleTop);
|
||||
|
||||
// GH#5029 - make sure to InvalidateAll here, so that we'll paint the entire visible viewport.
|
||||
try
|
||||
{
|
||||
_buffer->GetRenderTarget().TriggerRedrawAll();
|
||||
}
|
||||
CATCH_LOG();
|
||||
_NotifyScrollEvent();
|
||||
|
||||
return S_OK;
|
||||
|
||||
@@ -156,6 +156,7 @@ public:
|
||||
#pragma region IUiaData
|
||||
std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override;
|
||||
const bool IsSelectionActive() const noexcept override;
|
||||
const bool IsBlockSelection() const noexcept override;
|
||||
void ClearSelection() override;
|
||||
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
|
||||
const COORD GetSelectionAnchor() const noexcept override;
|
||||
|
||||
@@ -106,6 +106,11 @@ const bool Terminal::IsSelectionActive() const noexcept
|
||||
return _selection.has_value();
|
||||
}
|
||||
|
||||
const bool Terminal::IsBlockSelection() const noexcept
|
||||
{
|
||||
return _blockSelection;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Checks if the CopyOnSelect setting is active
|
||||
// Return Value:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -347,17 +347,14 @@ void ConptyRoundtripTests::WriteAFewSimpleLines()
|
||||
expectedOutput.push_back("AAA");
|
||||
expectedOutput.push_back("\r\n");
|
||||
expectedOutput.push_back("BBB");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Here, we're going to emit 3 spaces. The region that got invalidated was a
|
||||
// rectangle from 0,0 to 3,3, so the vt renderer will try to render the
|
||||
// region in between BBB and CCC as well, because it got included in the
|
||||
// rectangle Or() operation.
|
||||
// This behavior should not be seen as binding - if a future optimization
|
||||
// breaks this test, it wouldn't be the worst.
|
||||
expectedOutput.push_back(" ");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Jump down to the fourth line because emitting spaces didn't do anything
|
||||
// and we will skip to emitting the CCC segment.
|
||||
expectedOutput.push_back("\x1b[4;1H");
|
||||
expectedOutput.push_back("CCC");
|
||||
|
||||
// Cursor goes back on.
|
||||
expectedOutput.push_back("\x1b[?25h");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyData(termTb);
|
||||
@@ -458,14 +455,10 @@ void ConptyRoundtripTests::TestAdvancedWrapping()
|
||||
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
|
||||
// Without line breaking, write the remaining 20 chars
|
||||
expectedOutput.push_back(R"(qrstuvwxyz{|}~!"#$%&)");
|
||||
// Clear the rest of row 1
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
// This is the hard line break
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Now write row 2 of the buffer
|
||||
expectedOutput.push_back(" 1234567890");
|
||||
// and clear everything after the text, because the buffer is empty.
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb);
|
||||
@@ -537,8 +530,6 @@ void ConptyRoundtripTests::TestExactWrappingWithoutSpaces()
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Now write row 2 of the buffer
|
||||
expectedOutput.push_back("1234567890");
|
||||
// and clear everything after the text, because the buffer is empty.
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb);
|
||||
@@ -601,8 +592,6 @@ void ConptyRoundtripTests::TestExactWrappingWithSpaces()
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Now write row 2 of the buffer
|
||||
expectedOutput.push_back(" 1234567890");
|
||||
// and clear everything after the text, because the buffer is empty.
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb);
|
||||
@@ -639,7 +628,7 @@ void ConptyRoundtripTests::MoveCursorAtEOL()
|
||||
// might change, but the buffer contents shouldn't.
|
||||
// If they do change and these tests break, that's to be expected.
|
||||
expectedOutput.push_back(std::string(TerminalViewWidth, 'A'));
|
||||
expectedOutput.push_back("\x1b[1;80H");
|
||||
// expectedOutput.push_back("\x1b[1;80H");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
@@ -665,8 +654,11 @@ void ConptyRoundtripTests::MoveCursorAtEOL()
|
||||
|
||||
verifyData1(hostTb);
|
||||
|
||||
// expectedOutput.push_back(" ");
|
||||
// expectedOutput.push_back("\x1b[1;80H");
|
||||
expectedOutput.push_back("\x08");
|
||||
expectedOutput.push_back(" ");
|
||||
expectedOutput.push_back("\x1b[1;80H");
|
||||
expectedOutput.push_back("\x08");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyData1(termTb);
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace Microsoft::Terminal::Core;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace TerminalCoreUnitTests
|
||||
{
|
||||
@@ -21,66 +24,109 @@ namespace TerminalCoreUnitTests
|
||||
{
|
||||
TEST_CLASS(ScreenSizeLimitsTest);
|
||||
|
||||
TEST_METHOD(ScreenWidthAndHeightAreClampedToBounds)
|
||||
{
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
TEST_METHOD(ScreenWidthAndHeightAreClampedToBounds);
|
||||
TEST_METHOD(ScrollbackHistorySizeIsClampedToBounds);
|
||||
|
||||
// Negative values for initial visible row count or column count
|
||||
// are clamped to 1. Too-large positive values are clamped to SHRT_MAX.
|
||||
auto negativeColumnsSettings = winrt::make<MockTermSettings>(10000, 9999999, -1234);
|
||||
Terminal negativeColumnsTerminal;
|
||||
negativeColumnsTerminal.CreateFromSettings(negativeColumnsSettings, emptyRenderTarget);
|
||||
COORD actualDimensions = negativeColumnsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, SHRT_MAX, L"Row count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, 1, L"Column count clamped to 1");
|
||||
|
||||
// Zero values are clamped to 1 as well.
|
||||
auto zeroRowsSettings = winrt::make<MockTermSettings>(10000, 0, 9999999);
|
||||
Terminal zeroRowsTerminal;
|
||||
zeroRowsTerminal.CreateFromSettings(zeroRowsSettings, emptyRenderTarget);
|
||||
actualDimensions = zeroRowsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, 1, L"Row count clamped to 1");
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, SHRT_MAX, L"Column count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
}
|
||||
|
||||
TEST_METHOD(ScrollbackHistorySizeIsClampedToBounds)
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
// which is the *sum* of the history size plus the number of rows
|
||||
// actually visible on screen at the moment.
|
||||
|
||||
const unsigned int visibleRowCount = 100;
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
// Zero history size is acceptable.
|
||||
auto noHistorySettings = winrt::make<MockTermSettings>(0, visibleRowCount, 100);
|
||||
Terminal noHistoryTerminal;
|
||||
noHistoryTerminal.CreateFromSettings(noHistorySettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(noHistoryTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"History size of 0 is accepted");
|
||||
|
||||
// Negative history sizes are clamped to zero.
|
||||
auto negativeHistorySizeSettings = winrt::make<MockTermSettings>(-100, visibleRowCount, 100);
|
||||
Terminal negativeHistorySizeTerminal;
|
||||
negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(negativeHistorySizeTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"Negative history size is clamped to 0");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX is acceptable.
|
||||
auto maxHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount, visibleRowCount, 100);
|
||||
Terminal maxHistorySizeTerminal;
|
||||
maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(maxHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == SHRT_MAX - initial row count is accepted");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX + 1 will be clamped slightly.
|
||||
auto justTooBigHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount + 1, visibleRowCount, 100);
|
||||
Terminal justTooBigHistorySizeTerminal;
|
||||
justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(justTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == 1 + SHRT_MAX - initial row count is clamped to SHRT_MAX - initial row count");
|
||||
|
||||
// Ridiculously large history sizes are also clamped.
|
||||
auto farTooBigHistorySizeSettings = winrt::make<MockTermSettings>(99999999, visibleRowCount, 100);
|
||||
Terminal farTooBigHistorySizeTerminal;
|
||||
farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(farTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size that is far too large is clamped to SHRT_MAX - initial row count");
|
||||
}
|
||||
TEST_METHOD(ResizeIsClampedToBounds);
|
||||
};
|
||||
}
|
||||
|
||||
using namespace TerminalCoreUnitTests;
|
||||
|
||||
void ScreenSizeLimitsTest::ScreenWidthAndHeightAreClampedToBounds()
|
||||
{
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
// Negative values for initial visible row count or column count
|
||||
// are clamped to 1. Too-large positive values are clamped to SHRT_MAX.
|
||||
auto negativeColumnsSettings = winrt::make<MockTermSettings>(10000, 9999999, -1234);
|
||||
Terminal negativeColumnsTerminal;
|
||||
negativeColumnsTerminal.CreateFromSettings(negativeColumnsSettings, emptyRenderTarget);
|
||||
COORD actualDimensions = negativeColumnsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, SHRT_MAX, L"Row count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, 1, L"Column count clamped to 1");
|
||||
|
||||
// Zero values are clamped to 1 as well.
|
||||
auto zeroRowsSettings = winrt::make<MockTermSettings>(10000, 0, 9999999);
|
||||
Terminal zeroRowsTerminal;
|
||||
zeroRowsTerminal.CreateFromSettings(zeroRowsSettings, emptyRenderTarget);
|
||||
actualDimensions = zeroRowsTerminal.GetViewport().Dimensions();
|
||||
VERIFY_ARE_EQUAL(actualDimensions.Y, 1, L"Row count clamped to 1");
|
||||
VERIFY_ARE_EQUAL(actualDimensions.X, SHRT_MAX, L"Column count clamped to SHRT_MAX == " WCS(SHRT_MAX));
|
||||
}
|
||||
|
||||
void ScreenSizeLimitsTest::ScrollbackHistorySizeIsClampedToBounds()
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
// which is the *sum* of the history size plus the number of rows
|
||||
// actually visible on screen at the moment.
|
||||
|
||||
const unsigned int visibleRowCount = 100;
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
// Zero history size is acceptable.
|
||||
auto noHistorySettings = winrt::make<MockTermSettings>(0, visibleRowCount, 100);
|
||||
Terminal noHistoryTerminal;
|
||||
noHistoryTerminal.CreateFromSettings(noHistorySettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(noHistoryTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"History size of 0 is accepted");
|
||||
|
||||
// Negative history sizes are clamped to zero.
|
||||
auto negativeHistorySizeSettings = winrt::make<MockTermSettings>(-100, visibleRowCount, 100);
|
||||
Terminal negativeHistorySizeTerminal;
|
||||
negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(negativeHistorySizeTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"Negative history size is clamped to 0");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX is acceptable.
|
||||
auto maxHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount, visibleRowCount, 100);
|
||||
Terminal maxHistorySizeTerminal;
|
||||
maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(maxHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == SHRT_MAX - initial row count is accepted");
|
||||
|
||||
// History size + initial visible rows == SHRT_MAX + 1 will be clamped slightly.
|
||||
auto justTooBigHistorySizeSettings = winrt::make<MockTermSettings>(SHRT_MAX - visibleRowCount + 1, visibleRowCount, 100);
|
||||
Terminal justTooBigHistorySizeTerminal;
|
||||
justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(justTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size == 1 + SHRT_MAX - initial row count is clamped to SHRT_MAX - initial row count");
|
||||
|
||||
// Ridiculously large history sizes are also clamped.
|
||||
auto farTooBigHistorySizeSettings = winrt::make<MockTermSettings>(99999999, visibleRowCount, 100);
|
||||
Terminal farTooBigHistorySizeTerminal;
|
||||
farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(farTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX), L"History size that is far too large is clamped to SHRT_MAX - initial row count");
|
||||
}
|
||||
|
||||
void ScreenSizeLimitsTest::ResizeIsClampedToBounds()
|
||||
{
|
||||
// What is actually clamped is the number of rows in the internal history buffer,
|
||||
// which is the *sum* of the history size plus the number of rows
|
||||
// actually visible on screen at the moment.
|
||||
//
|
||||
// This is a test for GH#2630, GH#2815.
|
||||
|
||||
const unsigned int initialVisibleColCount = 50;
|
||||
const unsigned int initialVisibleRowCount = 50;
|
||||
const auto historySize = SHRT_MAX - (initialVisibleRowCount * 2);
|
||||
DummyRenderTarget emptyRenderTarget;
|
||||
|
||||
Log::Comment(L"Watch out - this test takes a while on debug, because "
|
||||
L"ResizeWithReflow takes a while on debug. This is expected.");
|
||||
|
||||
auto settings = winrt::make<MockTermSettings>(historySize, initialVisibleRowCount, initialVisibleColCount);
|
||||
Log::Comment(L"First create a terminal with fewer than SHRT_MAX lines");
|
||||
Terminal terminal;
|
||||
terminal.CreateFromSettings(settings, emptyRenderTarget);
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(historySize + initialVisibleRowCount));
|
||||
|
||||
Log::Comment(L"Resize the terminal to have exactly SHRT_MAX lines");
|
||||
VERIFY_SUCCEEDED(terminal.UserResize({ initialVisibleColCount, initialVisibleRowCount * 2 }));
|
||||
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX));
|
||||
|
||||
Log::Comment(L"Resize the terminal to have MORE than SHRT_MAX lines - we should clamp to SHRT_MAX");
|
||||
VERIFY_SUCCEEDED(terminal.UserResize({ initialVisibleColCount, initialVisibleRowCount * 3 }));
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(SHRT_MAX));
|
||||
|
||||
Log::Comment(L"Resize back down to the original size");
|
||||
VERIFY_SUCCEEDED(terminal.UserResize({ initialVisibleColCount, initialVisibleRowCount }));
|
||||
VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast<unsigned int>(historySize + initialVisibleRowCount));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -254,7 +254,7 @@ void IslandWindow::Initialize()
|
||||
void IslandWindow::OnSize(const UINT width, const UINT height)
|
||||
{
|
||||
// update the interop window size
|
||||
SetWindowPos(_interopWindowHandle, 0, 0, 0, width, height, SWP_SHOWWINDOW);
|
||||
SetWindowPos(_interopWindowHandle, nullptr, 0, 0, width, height, SWP_SHOWWINDOW);
|
||||
|
||||
if (_rootGrid)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
********************************************************/
|
||||
#include "pch.h"
|
||||
#include "NonClientIslandWindow.h"
|
||||
#include "../types/inc/ThemeUtils.h"
|
||||
#include "../types/inc/utils.hpp"
|
||||
#include "TerminalThemeHelpers.h"
|
||||
|
||||
@@ -568,9 +567,6 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept
|
||||
return _OnNcHitTest({ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) });
|
||||
case WM_PAINT:
|
||||
return _OnPaint();
|
||||
case WM_ACTIVATE:
|
||||
// If we do this every time we're activated, it should be close enough to correct.
|
||||
TerminalTrySetDarkTheme(_window.get());
|
||||
}
|
||||
|
||||
return IslandWindow::MessageHandler(message, wParam, lParam);
|
||||
@@ -639,7 +635,7 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept
|
||||
}
|
||||
|
||||
::FillRect(opaqueDc, &rcRest, _backgroundBrush.get());
|
||||
::BufferedPaintSetAlpha(buf, NULL, 255);
|
||||
::BufferedPaintSetAlpha(buf, nullptr, 255);
|
||||
::EndBufferedPaint(buf, TRUE);
|
||||
}
|
||||
|
||||
@@ -688,7 +684,7 @@ void NonClientIslandWindow::_UpdateFrameTheme() const
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_IF_FAILED(ThemeUtils::SetWindowFrameDarkMode(_window.get(), isDarkMode));
|
||||
LOG_IF_FAILED(TerminalTrySetDarkTheme(_window.get(), isDarkMode));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!-- Override GetPackagingOutputs to roll up all our dependencies.
|
||||
@@ -143,5 +143,5 @@
|
||||
</Target>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
|
||||
<Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets')" />
|
||||
<Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" />
|
||||
</Project>
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.0.0" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.3.191217003-prerelease" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.1-rc" targetFramework="native" />
|
||||
<package id="Terminal.ThemeHelpers" version="0.1.200305005" targetFramework="native" />
|
||||
<package id="Terminal.ThemeHelpers" version="0.2.200324001" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -2,7 +2,7 @@
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
@@ -86,7 +86,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\chromium;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\chromium;$(SolutionDir)\dep\dynamic_bitset;$(SolutionDir)\dep\libpopcnt;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;$(OpenConsoleDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile>
|
||||
<!-- Manually include the generated TerminalCore header's path, because
|
||||
@@ -147,6 +147,9 @@
|
||||
<None Include="PropertySheet.props" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" />
|
||||
<ProjectReference Include="..\TerminalApp\TerminalApp.vcxproj">
|
||||
<Project>{ca5cad1a-44bd-4ac7-ac72-f16e576fdd12}</Project>
|
||||
</ProjectReference>
|
||||
@@ -156,16 +159,18 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
</ImportGroup>
|
||||
<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.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.3.191217003-prerelease\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.0.0" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -5,16 +5,20 @@
|
||||
|
||||
#include "../TerminalApp/ColorScheme.h"
|
||||
#include "../TerminalApp/Profile.h"
|
||||
#include "../TerminalApp/CascadiaSettings.h"
|
||||
#include "../LocalTests_TerminalApp/JsonTestClass.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace TerminalApp;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
|
||||
namespace TerminalAppUnitTests
|
||||
{
|
||||
class JsonTests
|
||||
class JsonTests : public JsonTestClass
|
||||
{
|
||||
BEGIN_TEST_CLASS(JsonTests)
|
||||
TEST_CLASS_PROPERTY(L"ActivationContext", L"TerminalApp.Unit.Tests.manifest")
|
||||
@@ -26,10 +30,11 @@ namespace TerminalAppUnitTests
|
||||
TEST_METHOD(DiffProfile);
|
||||
TEST_METHOD(DiffProfileWithNull);
|
||||
|
||||
TEST_METHOD(TestWrongValueType);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
reader = std::unique_ptr<Json::CharReader>(Json::CharReaderBuilder::CharReaderBuilder().newCharReader());
|
||||
|
||||
InitializeJsonReader();
|
||||
// Use 4 spaces to indent instead of \t
|
||||
_builder.settings_["indentation"] = " ";
|
||||
return true;
|
||||
@@ -39,7 +44,6 @@ namespace TerminalAppUnitTests
|
||||
void VerifyParseFailed(std::string content);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Json::CharReader> reader;
|
||||
Json::StreamWriterBuilder _builder;
|
||||
};
|
||||
|
||||
@@ -47,7 +51,7 @@ namespace TerminalAppUnitTests
|
||||
{
|
||||
Json::Value root;
|
||||
std::string errs;
|
||||
const bool parseResult = reader->parse(content.c_str(), content.c_str() + content.size(), &root, &errs);
|
||||
const bool parseResult = _reader->parse(content.c_str(), content.c_str() + content.size(), &root, &errs);
|
||||
VERIFY_IS_TRUE(parseResult, winrt::to_hstring(errs).c_str());
|
||||
return root;
|
||||
}
|
||||
@@ -55,7 +59,7 @@ namespace TerminalAppUnitTests
|
||||
{
|
||||
Json::Value root;
|
||||
std::string errs;
|
||||
const bool parseResult = reader->parse(content.c_str(), content.c_str() + content.size(), &root, &errs);
|
||||
const bool parseResult = _reader->parse(content.c_str(), content.c_str() + content.size(), &root, &errs);
|
||||
VERIFY_IS_FALSE(parseResult);
|
||||
}
|
||||
|
||||
@@ -79,6 +83,7 @@ namespace TerminalAppUnitTests
|
||||
"\"brightRed\" : \"#E74856\","
|
||||
"\"brightWhite\" : \"#F2F2F2\","
|
||||
"\"brightYellow\" : \"#F9F1A5\","
|
||||
"\"cursorColor\" : \"#FFFFFF\","
|
||||
"\"cyan\" : \"#3A96DD\","
|
||||
"\"foreground\" : \"#F2F2F2\","
|
||||
"\"green\" : \"#13A10E\","
|
||||
@@ -96,6 +101,7 @@ namespace TerminalAppUnitTests
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0xf2, 0xf2, 0xf2), scheme.GetForeground());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x0c, 0x0c, 0x0c), scheme.GetBackground());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x13, 0x13, 0x13), scheme.GetSelectionBackground());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0xFF, 0xFF, 0xFF), scheme.GetCursorColor());
|
||||
|
||||
std::array<COLORREF, COLOR_TABLE_SIZE> expectedCampbellTable;
|
||||
auto campbellSpan = gsl::span<COLORREF>(&expectedCampbellTable[0], gsl::narrow<ptrdiff_t>(COLOR_TABLE_SIZE));
|
||||
@@ -219,4 +225,57 @@ namespace TerminalAppUnitTests
|
||||
VERIFY_IS_TRUE("bar" == diff["icon"].asString());
|
||||
}
|
||||
|
||||
void JsonTests::TestWrongValueType()
|
||||
{
|
||||
// This json blob has a whole bunch of settings with the wrong value
|
||||
// types - strings for int values, ints for strings, floats for ints,
|
||||
// etc. When we encounter data that's the wrong data type, we should
|
||||
// gracefully ignore it, as opposed to throwing an exception, causing us
|
||||
// to fail to load the settings at all.
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"defaultProfile" : "{00000000-1111-0000-0000-000000000000}",
|
||||
"profiles": [
|
||||
{
|
||||
"guid" : "{00000000-1111-0000-0000-000000000000}",
|
||||
"acrylicOpacity" : "0.5",
|
||||
"closeOnExit" : "true",
|
||||
"fontSize" : "10",
|
||||
"historySize" : 1234.5678,
|
||||
"padding" : 20,
|
||||
"snapOnInput" : "false",
|
||||
"icon" : 4,
|
||||
"backgroundImageOpacity": false,
|
||||
"useAcrylic" : 14
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings0Json = VerifyParseSucceeded(settings0String);
|
||||
|
||||
CascadiaSettings settings;
|
||||
|
||||
settings._ParseJsonString(settings0String, false);
|
||||
// We should not throw an exception trying to parse the settings here.
|
||||
settings.LayerJson(settings._userSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, settings._profiles.size());
|
||||
auto& profile = settings._profiles.at(0);
|
||||
Profile defaults{};
|
||||
|
||||
VERIFY_ARE_EQUAL(defaults._acrylicTransparency, profile._acrylicTransparency);
|
||||
VERIFY_ARE_EQUAL(defaults._closeOnExitMode, profile._closeOnExitMode);
|
||||
VERIFY_ARE_EQUAL(defaults._fontSize, profile._fontSize);
|
||||
VERIFY_ARE_EQUAL(defaults._historySize, profile._historySize);
|
||||
// A 20 as an int can still be treated as a json string
|
||||
VERIFY_ARE_EQUAL(L"20", profile._padding);
|
||||
VERIFY_ARE_EQUAL(defaults._snapOnInput, profile._snapOnInput);
|
||||
// 4 is a valid string value
|
||||
VERIFY_ARE_EQUAL(L"4", profile._icon);
|
||||
// false is not a valid optional<double>
|
||||
VERIFY_IS_FALSE(profile._backgroundImageOpacity.has_value());
|
||||
VERIFY_ARE_EQUAL(defaults._useAcrylic, profile._useAcrylic);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
@@ -82,7 +83,7 @@
|
||||
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\chromium;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\chromium;$(SolutionDir)\dep\dynamic_bitset;$(SolutionDir)\dep\libpopcnt;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>UNIT_TESTING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>INLINE_TEST_METHOD_MARKUP;UNIT_TESTING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\packages\Taef.Redist.Wlk.10.51.200127004\build\Taef.Redist.Wlk.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\packages\Taef.Redist.Wlk.10.51.200127004\build\Taef.Redist.Wlk.targets')" />
|
||||
|
||||
@@ -52,13 +52,13 @@
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<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.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<Import Project="..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.190730.2\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<!-- 17134 is RS4, 17763 is RS5, 18362 is 19H1 -->
|
||||
|
||||
@@ -212,7 +212,7 @@ void CursorBlinker::KillCaretTimer()
|
||||
|
||||
bRet = DeleteTimerQueueTimer(_hCaretBlinkTimerQueue,
|
||||
_hCaretBlinkTimer,
|
||||
NULL);
|
||||
nullptr);
|
||||
|
||||
// According to https://msdn.microsoft.com/en-us/library/windows/desktop/ms682569(v=vs.85).aspx
|
||||
// A failure to delete the timer with the LastError being ERROR_IO_PENDING means that the timer is
|
||||
|
||||
@@ -21,7 +21,7 @@ static bool ConhostV2ForcedInRegistry()
|
||||
// If the registry value doesn't exist, or exists and is non-zero, we should default to using the v2 console.
|
||||
// Otherwise, in the case of an explicit value of 0, we should use the legacy console.
|
||||
bool fShouldUseConhostV2 = true;
|
||||
PCSTR pszErrorDescription = NULL;
|
||||
PCSTR pszErrorDescription = nullptr;
|
||||
bool fIgnoreError = false;
|
||||
|
||||
// open HKCU\Console
|
||||
@@ -110,10 +110,10 @@ static bool ShouldUseLegacyConhost(const ConsoleArguments& args)
|
||||
// We expect legacy launches to be infrequent enough to not cause an issue.
|
||||
TraceLoggingWrite(g_ConhostLauncherProvider, "IsLegacyLoaded", TraceLoggingBool(true, "ConsoleLegacy"), TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY));
|
||||
|
||||
PCWSTR pszConhostDllName = L"ConhostV1.dll";
|
||||
const PCWSTR pszConhostDllName = L"ConhostV1.dll";
|
||||
|
||||
// Load our implementation, and then Load/Launch the IO thread.
|
||||
wil::unique_hmodule hConhostBin(LoadLibraryExW(pszConhostDllName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32));
|
||||
wil::unique_hmodule hConhostBin(LoadLibraryExW(pszConhostDllName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32));
|
||||
if (hConhostBin.get() != nullptr)
|
||||
{
|
||||
typedef NTSTATUS (*PFNCONSOLECREATEIOTHREAD)(__in HANDLE Server);
|
||||
|
||||
@@ -153,7 +153,7 @@ void TestGetConsoleAliasHelper(TCH* ptszSourceGiven,
|
||||
// This is strange because it's a scope exit so we need to declare in the parent scope, then let it go if we didn't actually need it.
|
||||
// I just prefer keeping the exit next to the allocation so it doesn't get lost.
|
||||
auto removeAliasOnExit = wil::scope_exit([&] {
|
||||
AddConsoleAliasT(ptszSource, NULL, ptszExeName);
|
||||
AddConsoleAliasT(ptszSource, nullptr, ptszExeName);
|
||||
});
|
||||
if (!bSetFirst)
|
||||
{
|
||||
|
||||
@@ -219,7 +219,7 @@ void BufferTests::ChafaGifPerformance()
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
// Taken from: https://blog.kowalczyk.info/article/zy/Embedding-binary-resources-on-Windows.html
|
||||
HGLOBAL res_handle = NULL;
|
||||
HGLOBAL res_handle = nullptr;
|
||||
HRSRC res;
|
||||
char* res_data;
|
||||
DWORD res_size;
|
||||
|
||||
@@ -61,7 +61,7 @@ class FileTests
|
||||
END_TEST_METHOD();*/
|
||||
};
|
||||
|
||||
static HANDLE _cancellationEvent = 0;
|
||||
static HANDLE _cancellationEvent = nullptr;
|
||||
|
||||
bool FileTests::ClassSetup()
|
||||
{
|
||||
|
||||
@@ -243,7 +243,7 @@ void InputTests::TestPeekConsoleInvalid()
|
||||
void InputTests::TestReadConsoleInvalid()
|
||||
{
|
||||
DWORD nRead = (DWORD)-1;
|
||||
VERIFY_WIN32_BOOL_FAILED(ReadConsoleInput(0, nullptr, 0, &nRead));
|
||||
VERIFY_WIN32_BOOL_FAILED(ReadConsoleInput(nullptr, nullptr, 0, &nRead));
|
||||
VERIFY_ARE_EQUAL(nRead, (DWORD)0);
|
||||
|
||||
nRead = (DWORD)-1;
|
||||
@@ -275,7 +275,7 @@ void InputTests::TestReadConsoleInvalid()
|
||||
void InputTests::TestWriteConsoleInvalid()
|
||||
{
|
||||
DWORD nWrite = (DWORD)-1;
|
||||
VERIFY_WIN32_BOOL_FAILED(WriteConsoleInput(0, nullptr, 0, &nWrite));
|
||||
VERIFY_WIN32_BOOL_FAILED(WriteConsoleInput(nullptr, nullptr, 0, &nWrite));
|
||||
VERIFY_ARE_EQUAL(nWrite, (DWORD)0);
|
||||
|
||||
// weird: WriteConsoleInput with INVALID_HANDLE_VALUE writes garbage to lpNumberOfEventsWritten, whereas
|
||||
@@ -358,7 +358,7 @@ void InputTests::TestReadConsolePasswordScenario()
|
||||
{
|
||||
wchar_t ch;
|
||||
DWORD c;
|
||||
int err = ReadConsoleW(hIn, &ch, 1, &c, 0);
|
||||
int err = ReadConsoleW(hIn, &ch, 1, &c, nullptr);
|
||||
|
||||
if (!err || c != 1)
|
||||
{
|
||||
|
||||
@@ -186,9 +186,9 @@ bool Common::TestBufferSetup()
|
||||
|
||||
_hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
|
||||
0 /*dwShareMode*/,
|
||||
NULL /*lpSecurityAttributes*/,
|
||||
nullptr /*lpSecurityAttributes*/,
|
||||
CONSOLE_TEXTMODE_BUFFER,
|
||||
NULL /*lpReserved*/);
|
||||
nullptr /*lpReserved*/);
|
||||
|
||||
VERIFY_ARE_NOT_EQUAL(_hConsole, INVALID_HANDLE_VALUE, L"Creating our test screen buffer.");
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@ void CommandHistory::Empty()
|
||||
{
|
||||
_commands.clear();
|
||||
LastDisplayed = -1;
|
||||
Flags = CLE_RESET;
|
||||
WI_SetFlag(Flags, CLE_RESET);
|
||||
}
|
||||
|
||||
bool CommandHistory::AtFirstCommand() const
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
CLIENT_ID ClientId;
|
||||
ClientId.UniqueProcess = UlongToHandle(*ProcessId);
|
||||
ClientId.UniqueThread = 0;
|
||||
ClientId.UniqueThread = nullptr;
|
||||
|
||||
HANDLE ProcessHandle;
|
||||
NTSTATUS Status = s_NtOpenProcess(&ProcessHandle, PROCESS_QUERY_LIMITED_INFORMATION, &oa, &ClientId);
|
||||
|
||||
@@ -123,7 +123,12 @@ COORD RenderData::GetCursorPosition() const noexcept
|
||||
{
|
||||
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
const auto& cursor = gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor();
|
||||
return cursor.GetPosition();
|
||||
COORD effectiveCursor = cursor.GetPosition();
|
||||
if (cursor.IsDelayedEOLWrap() && cursor.GetDelayedAtPosition() == effectiveCursor)
|
||||
{
|
||||
effectiveCursor.X++;
|
||||
}
|
||||
return effectiveCursor;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -359,6 +364,11 @@ const bool RenderData::IsSelectionActive() const
|
||||
return Selection::Instance().IsAreaSelected();
|
||||
}
|
||||
|
||||
const bool RenderData::IsBlockSelection() const noexcept
|
||||
{
|
||||
return !Selection::Instance().IsLineSelection();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - If a selection exists, clears it and restores the state.
|
||||
// Will also unblock a blocked write if one exists.
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
#pragma region IUiaData
|
||||
const bool IsSelectionActive() const override;
|
||||
const bool IsBlockSelection() const noexcept override;
|
||||
void ClearSelection() override;
|
||||
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
|
||||
const COORD GetSelectionAnchor() const noexcept;
|
||||
|
||||
@@ -269,7 +269,7 @@ void ConsoleCheckDebug()
|
||||
ServerInformation.InputAvailableEvent = ServiceLocator::LocateGlobals().hInputEvent.get();
|
||||
RETURN_IF_FAILED(g.pDeviceComm->SetServerInformation(&ServerInformation));
|
||||
|
||||
HANDLE const hThread = CreateThread(nullptr, 0, ConsoleIoThread, 0, 0, nullptr);
|
||||
HANDLE const hThread = CreateThread(nullptr, 0, ConsoleIoThread, nullptr, 0, nullptr);
|
||||
RETURN_HR_IF(E_HANDLE, hThread == nullptr);
|
||||
LOG_IF_WIN32_BOOL_FALSE(CloseHandle(hThread)); // The thread will run on its own and close itself. Free the associated handle.
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class CommandLineTests
|
||||
m_state->PrepareGlobalInputBuffer();
|
||||
m_state->PrepareReadHandle();
|
||||
m_state->PrepareCookedReadData();
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", (HANDLE)0);
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr);
|
||||
if (!m_pHistory)
|
||||
{
|
||||
return false;
|
||||
@@ -54,7 +54,7 @@ class CommandLineTests
|
||||
|
||||
TEST_METHOD_CLEANUP(MethodCleanup)
|
||||
{
|
||||
CommandHistory::s_Free((HANDLE)0);
|
||||
CommandHistory::s_Free(nullptr);
|
||||
m_pHistory = nullptr;
|
||||
m_state->CleanupCookedReadData();
|
||||
m_state->CleanupReadHandle();
|
||||
|
||||
@@ -50,7 +50,7 @@ class CommandListPopupTests
|
||||
m_state->PrepareGlobalInputBuffer();
|
||||
m_state->PrepareReadHandle();
|
||||
m_state->PrepareCookedReadData();
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", (HANDLE)0);
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr);
|
||||
// resize command history storage to 50 items so that we don't cycle on accident
|
||||
// when PopupTestHelper::InitLongHistory() is called.
|
||||
CommandHistory::s_ResizeAll(50);
|
||||
@@ -63,7 +63,7 @@ class CommandListPopupTests
|
||||
|
||||
TEST_METHOD_CLEANUP(MethodCleanup)
|
||||
{
|
||||
CommandHistory::s_Free((HANDLE)0);
|
||||
CommandHistory::s_Free(nullptr);
|
||||
m_pHistory = nullptr;
|
||||
m_state->CleanupCookedReadData();
|
||||
m_state->CleanupReadHandle();
|
||||
|
||||
@@ -46,7 +46,7 @@ class CommandNumberPopupTests
|
||||
m_state->PrepareGlobalInputBuffer();
|
||||
m_state->PrepareReadHandle();
|
||||
m_state->PrepareCookedReadData();
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", (HANDLE)0);
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr);
|
||||
if (!m_pHistory)
|
||||
{
|
||||
return false;
|
||||
@@ -56,7 +56,7 @@ class CommandNumberPopupTests
|
||||
|
||||
TEST_METHOD_CLEANUP(MethodCleanup)
|
||||
{
|
||||
CommandHistory::s_Free((HANDLE)0);
|
||||
CommandHistory::s_Free(nullptr);
|
||||
m_pHistory = nullptr;
|
||||
m_state->CleanupCookedReadData();
|
||||
m_state->CleanupReadHandle();
|
||||
|
||||
@@ -113,6 +113,7 @@ class ConptyOutputTests
|
||||
TEST_METHOD(SimpleWriteOutputTest);
|
||||
TEST_METHOD(WriteTwoLinesUsesNewline);
|
||||
TEST_METHOD(WriteAFewSimpleLines);
|
||||
TEST_METHOD(InvalidateUntilOneBeforeEnd);
|
||||
|
||||
private:
|
||||
bool _writeCallback(const char* const pch, size_t const cch);
|
||||
@@ -124,10 +125,14 @@ private:
|
||||
|
||||
bool ConptyOutputTests::_writeCallback(const char* const pch, size_t const cch)
|
||||
{
|
||||
// Since rendering happens on a background thread that doesn't have the exception handler on it
|
||||
// we need to rely on VERIFY's return codes instead of exceptions.
|
||||
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;
|
||||
|
||||
std::string actualString = std::string(pch, cch);
|
||||
VERIFY_IS_GREATER_THAN(expectedOutput.size(),
|
||||
static_cast<size_t>(0),
|
||||
NoThrowString().Format(L"writing=\"%hs\", expecting %u strings", actualString.c_str(), expectedOutput.size()));
|
||||
RETURN_BOOL_IF_FALSE(VERIFY_IS_GREATER_THAN(expectedOutput.size(),
|
||||
static_cast<size_t>(0),
|
||||
NoThrowString().Format(L"writing=\"%hs\", expecting %u strings", actualString.c_str(), expectedOutput.size())));
|
||||
|
||||
std::string first = expectedOutput.front();
|
||||
expectedOutput.pop_front();
|
||||
@@ -135,8 +140,8 @@ bool ConptyOutputTests::_writeCallback(const char* const pch, size_t const cch)
|
||||
Log::Comment(NoThrowString().Format(L"Expected =\t\"%hs\"", first.c_str()));
|
||||
Log::Comment(NoThrowString().Format(L"Actual =\t\"%hs\"", actualString.c_str()));
|
||||
|
||||
VERIFY_ARE_EQUAL(first.length(), cch);
|
||||
VERIFY_ARE_EQUAL(first, actualString);
|
||||
RETURN_BOOL_IF_FALSE(VERIFY_ARE_EQUAL(first.length(), cch));
|
||||
RETURN_BOOL_IF_FALSE(VERIFY_ARE_EQUAL(first, actualString));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -301,16 +306,65 @@ void ConptyOutputTests::WriteAFewSimpleLines()
|
||||
expectedOutput.push_back("AAA");
|
||||
expectedOutput.push_back("\r\n");
|
||||
expectedOutput.push_back("BBB");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Here, we're going to emit 3 spaces. The region that got invalidated was a
|
||||
// rectangle from 0,0 to 3,3, so the vt renderer will try to render the
|
||||
// region in between BBB and CCC as well, because it got included in the
|
||||
// rectangle Or() operation.
|
||||
// This behavior should not be seen as binding - if a future optimization
|
||||
// breaks this test, it wouldn't be the worst.
|
||||
expectedOutput.push_back(" ");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Jump down to the fourth line because emitting spaces didn't do anything
|
||||
// and we will skip to emitting the CCC segment.
|
||||
expectedOutput.push_back("\x1b[4;1H");
|
||||
expectedOutput.push_back("CCC");
|
||||
|
||||
// Cursor goes back on.
|
||||
expectedOutput.push_back("\x1b[?25h");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
void ConptyOutputTests::InvalidateUntilOneBeforeEnd()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure we don't use EL and wipe out the last column of text"));
|
||||
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
auto& tb = si.GetTextBuffer();
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
// Move the cursor to width-15, draw 15 characters
|
||||
sm.ProcessString(L"\x1b[1;66H");
|
||||
sm.ProcessString(L"ABCDEFGHIJKLMNO");
|
||||
|
||||
{
|
||||
auto iter = tb.GetCellDataAt({ 78, 0 });
|
||||
VERIFY_ARE_EQUAL(L"N", (iter++)->Chars());
|
||||
VERIFY_ARE_EQUAL(L"O", (iter++)->Chars());
|
||||
}
|
||||
|
||||
expectedOutput.push_back("\x1b[65C");
|
||||
expectedOutput.push_back("ABCDEFGHIJKLMNO");
|
||||
expectedOutput.push_back("\x1b[1;80H"); // we move the cursor to the end of the line after paint
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
// overstrike the first with X and the middle 8 with spaces
|
||||
sm.ProcessString(L"\x1b[1;66H");
|
||||
// ABCDEFGHIJKLMNO
|
||||
sm.ProcessString(L"X ");
|
||||
|
||||
{
|
||||
auto iter = tb.GetCellDataAt({ 78, 0 });
|
||||
VERIFY_ARE_EQUAL(L" ", (iter++)->Chars());
|
||||
VERIFY_ARE_EQUAL(L"O", (iter++)->Chars());
|
||||
}
|
||||
|
||||
expectedOutput.push_back("\x1b[1;66H");
|
||||
expectedOutput.push_back("X"); // sequence optimizer should choose ECH here
|
||||
expectedOutput.push_back("\x1b[13X");
|
||||
expectedOutput.push_back("\x1b[13C");
|
||||
|
||||
expectedOutput.push_back("\x1b[?25h"); // we turn the cursor back on for good measure
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
@@ -689,7 +689,7 @@ void ConsoleArgumentsTests::IsVtHandleValidTests()
|
||||
// We use both 0 and INVALID_HANDLE_VALUE as invalid handles since we're not sure
|
||||
// exactly what will get passed in on the STDIN/STDOUT handles as it can vary wildly
|
||||
// depending on who is passing it.
|
||||
VERIFY_IS_FALSE(IsValidHandle(0), L"Zero handle invalid.");
|
||||
VERIFY_IS_FALSE(IsValidHandle(nullptr), L"Zero handle invalid.");
|
||||
VERIFY_IS_FALSE(IsValidHandle(INVALID_HANDLE_VALUE), L"Invalid handle invalid.");
|
||||
VERIFY_IS_TRUE(IsValidHandle(UlongToHandle(0x4)), L"0x4 is valid.");
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class CopyToCharPopupTests
|
||||
m_state->PrepareGlobalInputBuffer();
|
||||
m_state->PrepareReadHandle();
|
||||
m_state->PrepareCookedReadData();
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", (HANDLE)0);
|
||||
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", nullptr);
|
||||
if (!m_pHistory)
|
||||
{
|
||||
return false;
|
||||
@@ -55,7 +55,7 @@ class CopyToCharPopupTests
|
||||
|
||||
TEST_METHOD_CLEANUP(MethodCleanup)
|
||||
{
|
||||
CommandHistory::s_Free((HANDLE)0);
|
||||
CommandHistory::s_Free(nullptr);
|
||||
m_pHistory = nullptr;
|
||||
m_state->CleanupCookedReadData();
|
||||
m_state->CleanupReadHandle();
|
||||
|
||||
@@ -148,6 +148,7 @@ class TextBufferTests
|
||||
|
||||
void WriteLinesToBuffer(const std::vector<std::wstring>& text, TextBuffer& buffer);
|
||||
TEST_METHOD(GetWordBoundaries);
|
||||
TEST_METHOD(GetGlyphBoundaries);
|
||||
|
||||
TEST_METHOD(GetTextRects);
|
||||
TEST_METHOD(GetText);
|
||||
@@ -2153,6 +2154,59 @@ void TextBufferTests::GetWordBoundaries()
|
||||
}
|
||||
}
|
||||
|
||||
void TextBufferTests::GetGlyphBoundaries()
|
||||
{
|
||||
struct ExpectedResult
|
||||
{
|
||||
std::wstring name;
|
||||
til::point start;
|
||||
til::point wideGlyphEnd;
|
||||
til::point normalEnd;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
const std::vector<ExpectedResult> expected = {
|
||||
{ L"Buffer Start", { 0, 0 }, { 2, 0 }, { 1, 0 } },
|
||||
{ L"Line Start", { 0, 1 }, { 2, 1 }, { 1, 1 } },
|
||||
{ L"General Case", { 1, 1 }, { 3, 1 }, { 2, 1 } },
|
||||
{ L"Line End", { 9, 1 }, { 0, 2 }, { 0, 2 } },
|
||||
{ L"Buffer End", { 9, 9 }, { 0, 10 }, { 0, 10 } },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:wideGlyph", L"{false, true}")
|
||||
END_TEST_METHOD_PROPERTIES();
|
||||
|
||||
bool wideGlyph;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"wideGlyph", wideGlyph), L"Get wide glyph variant");
|
||||
|
||||
COORD bufferSize{ 10, 10 };
|
||||
UINT cursorSize = 12;
|
||||
TextAttribute attr{ 0x7f };
|
||||
auto _buffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, _renderTarget);
|
||||
|
||||
// This is the burrito emoji: 🌯
|
||||
// It's encoded in UTF-16, as needed by the buffer.
|
||||
const auto burrito = L"\xD83C\xDF2F";
|
||||
const wchar_t* const output = wideGlyph ? burrito : L"X";
|
||||
|
||||
const OutputCellIterator iter{ output };
|
||||
|
||||
for (const auto& test : expected)
|
||||
{
|
||||
Log::Comment(test.name.c_str());
|
||||
auto target = test.start;
|
||||
_buffer->Write(iter, target);
|
||||
|
||||
auto start = _buffer->GetGlyphStart(target);
|
||||
auto end = _buffer->GetGlyphEnd(target);
|
||||
|
||||
VERIFY_ARE_EQUAL(test.start, start);
|
||||
VERIFY_ARE_EQUAL(wideGlyph ? test.wideGlyphEnd : test.normalEnd, end);
|
||||
}
|
||||
}
|
||||
|
||||
void TextBufferTests::GetTextRects()
|
||||
{
|
||||
// GetTextRects() is used to...
|
||||
|
||||
@@ -32,7 +32,7 @@ class UtilsTests
|
||||
m_state->PrepareGlobalFont();
|
||||
m_state->PrepareGlobalScreenBuffer();
|
||||
|
||||
UINT const seed = (UINT)time(NULL);
|
||||
UINT const seed = (UINT)time(nullptr);
|
||||
Log::Comment(String().Format(L"Setting random seed to : %d", seed));
|
||||
srand(seed);
|
||||
|
||||
|
||||
@@ -257,21 +257,22 @@ void VtRendererTest::Xterm256TestInvalidate()
|
||||
VERIFY_IS_FALSE(engine->_firstPaint);
|
||||
});
|
||||
|
||||
Viewport view = SetUpViewport();
|
||||
const Viewport view = SetUpViewport();
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating all invalidates the whole viewport."));
|
||||
VERIFY_SUCCEEDED(engine->InvalidateAll());
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating anything only invalidates that portion"));
|
||||
SMALL_RECT invalid = { 1, 1, 1, 1 };
|
||||
SMALL_RECT invalid = { 1, 1, 2, 2 };
|
||||
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.one());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
@@ -284,7 +285,9 @@ void VtRendererTest::Xterm256TestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
qExpectedInput.push_back("\x1b[H"); // Go Home
|
||||
qExpectedInput.push_back("\x1b[L"); // insert a line
|
||||
|
||||
@@ -300,7 +303,17 @@ void VtRendererTest::Xterm256TestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
// We would expect a CUP here, but the cursor is already at the home position
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
@@ -314,7 +327,9 @@ void VtRendererTest::Xterm256TestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
|
||||
qExpectedInput.push_back("\x1b[32;1H"); // Bottom of buffer
|
||||
qExpectedInput.push_back("\n"); // Scroll down once
|
||||
@@ -329,7 +344,17 @@ void VtRendererTest::Xterm256TestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
// We would expect a CUP here, but we're already at the bottom from the last call.
|
||||
qExpectedInput.push_back("\n\n\n"); // Scroll down three times
|
||||
@@ -349,7 +374,18 @@ void VtRendererTest::Xterm256TestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
qExpectedInput.push_back("\x1b[H"); // Go to home
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
@@ -357,21 +393,39 @@ void VtRendererTest::Xterm256TestInvalidate()
|
||||
|
||||
scrollDelta = { 0, 1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
scrollDelta = { 0, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
qExpectedInput.push_back("\x1b[2J");
|
||||
TestPaint(*engine, [&]() {
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"---- Scrolled one down and one up, nothing should change ----"
|
||||
L" But it still does for now MSFT:14169294"));
|
||||
invalid = view.ToExclusive();
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// only the bottom line should be dirty.
|
||||
// When we scrolled down, the bitmap looked like this:
|
||||
// 1111
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// And then we scrolled up and the top line fell off and a bottom
|
||||
// line was filled in like this:
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// 1111
|
||||
const til::rectangle expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
|
||||
VERIFY_ARE_EQUAL(expected, invalidRect);
|
||||
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
});
|
||||
@@ -730,15 +784,16 @@ void VtRendererTest::XtermTestInvalidate()
|
||||
L"Make sure that invalidating all invalidates the whole viewport."));
|
||||
VERIFY_SUCCEEDED(engine->InvalidateAll());
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating anything only invalidates that portion"));
|
||||
SMALL_RECT invalid = { 1, 1, 1, 1 };
|
||||
SMALL_RECT invalid = { 1, 1, 2, 2 };
|
||||
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.one());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
@@ -751,7 +806,9 @@ void VtRendererTest::XtermTestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
|
||||
qExpectedInput.push_back("\x1b[H"); // Go Home
|
||||
qExpectedInput.push_back("\x1b[L"); // insert a line
|
||||
@@ -766,7 +823,17 @@ void VtRendererTest::XtermTestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
// We would expect a CUP here, but the cursor is already at the home position
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
@@ -780,7 +847,9 @@ void VtRendererTest::XtermTestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
|
||||
qExpectedInput.push_back("\x1b[32;1H"); // Bottom of buffer
|
||||
qExpectedInput.push_back("\n"); // Scroll down once
|
||||
@@ -795,7 +864,17 @@ void VtRendererTest::XtermTestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
// We would expect a CUP here, but we're already at the bottom from the last call.
|
||||
qExpectedInput.push_back("\n\n\n"); // Scroll down three times
|
||||
@@ -815,7 +894,18 @@ void VtRendererTest::XtermTestInvalidate()
|
||||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
qExpectedInput.push_back("\x1b[H"); // Go to home
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
@@ -823,21 +913,39 @@ void VtRendererTest::XtermTestInvalidate()
|
||||
|
||||
scrollDelta = { 0, 1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
scrollDelta = { 0, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
qExpectedInput.push_back("\x1b[2J");
|
||||
TestPaint(*engine, [&]() {
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"---- Scrolled one down and one up, nothing should change ----"
|
||||
L" But it still does for now MSFT:14169294"));
|
||||
invalid = view.ToExclusive();
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// only the bottom line should be dirty.
|
||||
// When we scrolled down, the bitmap looked like this:
|
||||
// 1111
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// And then we scrolled up and the top line fell off and a bottom
|
||||
// line was filled in like this:
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// 1111
|
||||
const til::rectangle expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
|
||||
VERIFY_ARE_EQUAL(expected, invalidRect);
|
||||
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
});
|
||||
@@ -1051,15 +1159,15 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
||||
L"Make sure that invalidating all invalidates the whole viewport."));
|
||||
VERIFY_SUCCEEDED(engine->InvalidateAll());
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating anything only invalidates that portion"));
|
||||
SMALL_RECT invalid = { 1, 1, 1, 1 };
|
||||
SMALL_RECT invalid = { 1, 1, 2, 2 };
|
||||
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
@@ -1067,7 +1175,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
||||
COORD scrollDelta = { 0, 1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL); // sentinel
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
@@ -1076,7 +1184,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
||||
scrollDelta = { 0, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
@@ -1085,7 +1193,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
||||
scrollDelta = { 1, 0 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
@@ -1094,7 +1202,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
||||
scrollDelta = { -1, 0 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
@@ -1103,7 +1211,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
||||
scrollDelta = { 1, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
@@ -1351,7 +1459,7 @@ void VtRendererTest::TestResize()
|
||||
VERIFY_SUCCEEDED(engine->UpdateViewport(newView.ToInclusive()));
|
||||
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(newView, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
VERIFY_IS_FALSE(engine->_firstPaint);
|
||||
VERIFY_IS_FALSE(engine->_suppressResizeRepaint);
|
||||
});
|
||||
|
||||
@@ -79,12 +79,25 @@
|
||||
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
|
||||
#include <intsafe.h>
|
||||
|
||||
// LibPopCnt - Fast C/C++ bit population count library (on bits in an array)
|
||||
#include <libpopcnt.h>
|
||||
|
||||
// Dynamic Bitset (optional dependency on LibPopCnt for perf at bit counting)
|
||||
// Variable-size compressed-storage header-only bit flag storage library.
|
||||
#include <dynamic_bitset.hpp>
|
||||
|
||||
// SAL
|
||||
#include <sal.h>
|
||||
|
||||
// WRL
|
||||
#include <wrl.h>
|
||||
|
||||
// WEX/TAEF testing
|
||||
// Include before TIL if we're unit testing so it can light up WEX/TAEF template extensions
|
||||
#ifdef UNIT_TESTING
|
||||
#include <WexTestClass.h>
|
||||
#endif
|
||||
|
||||
// TIL - Terminal Implementation Library
|
||||
#ifndef BLOCK_TIL // Certain projects may want to include TIL manually to gain superpowers
|
||||
#include "til.h"
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define _TIL_INLINEPREFIX __declspec(noinline) inline
|
||||
|
||||
#include "til/at.h"
|
||||
#include "til/color.h"
|
||||
#include "til/some.h"
|
||||
#include "til/point.h"
|
||||
#include "til/size.h"
|
||||
#include "til/point.h"
|
||||
#include "til/rectangle.h"
|
||||
#include "til/operators.h"
|
||||
#include "til/bitmap.h"
|
||||
#include "til/u8u16convert.h"
|
||||
|
||||
//#include "til/operators.h"
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,345 +3,446 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "size.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class BitmapTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
class const_bitterator // Bit Iterator. Bitterator.
|
||||
namespace details
|
||||
{
|
||||
public:
|
||||
const_bitterator(const std::vector<bool>& values, size_t pos) :
|
||||
_map(values),
|
||||
_pos(pos)
|
||||
class _bitmap_const_iterator
|
||||
{
|
||||
}
|
||||
public:
|
||||
using iterator_category = typename std::input_iterator_tag;
|
||||
using value_type = typename const til::rectangle;
|
||||
using difference_type = typename ptrdiff_t;
|
||||
using pointer = typename const til::rectangle*;
|
||||
using reference = typename const til::rectangle&;
|
||||
|
||||
const_bitterator operator+(const ptrdiff_t movement)
|
||||
{
|
||||
auto copy = *this;
|
||||
copy += movement;
|
||||
return copy;
|
||||
}
|
||||
|
||||
const_bitterator operator-(const ptrdiff_t movement)
|
||||
{
|
||||
auto copy = *this;
|
||||
copy -= movement;
|
||||
return copy;
|
||||
}
|
||||
|
||||
const_bitterator& operator++()
|
||||
{
|
||||
++_pos;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
const_bitterator& operator--()
|
||||
{
|
||||
--_pos;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
const_bitterator& operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
_pos += movement;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
const_bitterator& operator-=(const ptrdiff_t& movement)
|
||||
{
|
||||
_pos -= movement;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
bool operator==(const const_bitterator& other) const
|
||||
{
|
||||
return _pos == other._pos && _map == other._map;
|
||||
}
|
||||
|
||||
bool operator!=(const const_bitterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const const_bitterator& other) const
|
||||
{
|
||||
return _pos < other._pos;
|
||||
}
|
||||
|
||||
bool operator>(const const_bitterator& other) const
|
||||
{
|
||||
return _pos > other._pos;
|
||||
}
|
||||
|
||||
bool operator*() const
|
||||
{
|
||||
return _map[_pos];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t _pos;
|
||||
const std::vector<bool>& _map;
|
||||
};
|
||||
|
||||
class const_runerator // Run Iterator. Runerator.
|
||||
{
|
||||
public:
|
||||
const_runerator(const std::vector<bool>& values, til::size sz, size_t pos) :
|
||||
_values(values),
|
||||
_size(sz),
|
||||
_pos(pos)
|
||||
{
|
||||
_calculateArea();
|
||||
}
|
||||
|
||||
const_runerator& operator++()
|
||||
{
|
||||
_pos = _nextPos;
|
||||
_calculateArea();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
bool operator==(const const_runerator& other) const
|
||||
{
|
||||
return _pos == other._pos && _values == other._values;
|
||||
}
|
||||
|
||||
bool operator!=(const const_runerator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const const_runerator& other) const
|
||||
{
|
||||
return _pos < other._pos;
|
||||
}
|
||||
|
||||
bool operator>(const const_runerator& other) const
|
||||
{
|
||||
return _pos > other._pos;
|
||||
}
|
||||
|
||||
til::rectangle operator*() const
|
||||
{
|
||||
return _run;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<bool>& _values;
|
||||
const til::size _size;
|
||||
size_t _pos;
|
||||
size_t _nextPos;
|
||||
til::rectangle _run;
|
||||
|
||||
til::point _indexToPoint(size_t index)
|
||||
{
|
||||
return til::point{ (ptrdiff_t)index % _size.width(), (ptrdiff_t)index / _size.width() };
|
||||
}
|
||||
|
||||
void _calculateArea()
|
||||
{
|
||||
const size_t end = (size_t)_size.area();
|
||||
|
||||
_nextPos = _pos;
|
||||
|
||||
while (_nextPos < end && !_values.at(_nextPos))
|
||||
_bitmap_const_iterator(const dynamic_bitset<>& values, til::rectangle rc, ptrdiff_t pos) :
|
||||
_values(values),
|
||||
_rc(rc),
|
||||
_pos(pos),
|
||||
_end(rc.size().area())
|
||||
{
|
||||
++_nextPos;
|
||||
_calculateArea();
|
||||
}
|
||||
|
||||
if (_nextPos < end)
|
||||
{
|
||||
// pos is now at the first on bit.
|
||||
const auto runStart = _indexToPoint(_nextPos);
|
||||
const size_t rowEndIndex = (size_t)((runStart.y() + 1) * _size.width());
|
||||
|
||||
ptrdiff_t runLength = 0;
|
||||
|
||||
do
|
||||
{
|
||||
++_nextPos;
|
||||
++runLength;
|
||||
} while (_nextPos < end && _nextPos < rowEndIndex && _values.at(_nextPos));
|
||||
|
||||
_run = til::rectangle{ runStart, til::size{ runLength, static_cast<ptrdiff_t>(1) } };
|
||||
}
|
||||
else
|
||||
_bitmap_const_iterator& operator++()
|
||||
{
|
||||
_pos = _nextPos;
|
||||
_run = til::rectangle{};
|
||||
_calculateArea();
|
||||
return (*this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_bitmap_const_iterator operator++(int)
|
||||
{
|
||||
const auto prev = *this;
|
||||
++*this;
|
||||
return prev;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const _bitmap_const_iterator& other) const noexcept
|
||||
{
|
||||
return _pos == other._pos && _values == other._values;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const _bitmap_const_iterator& other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr bool operator<(const _bitmap_const_iterator& other) const noexcept
|
||||
{
|
||||
return _pos < other._pos;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const _bitmap_const_iterator& other) const noexcept
|
||||
{
|
||||
return _pos > other._pos;
|
||||
}
|
||||
|
||||
constexpr reference operator*() const noexcept
|
||||
{
|
||||
return _run;
|
||||
}
|
||||
|
||||
constexpr pointer operator->() const noexcept
|
||||
{
|
||||
return &_run;
|
||||
}
|
||||
|
||||
private:
|
||||
const dynamic_bitset<>& _values;
|
||||
const til::rectangle _rc;
|
||||
ptrdiff_t _pos;
|
||||
ptrdiff_t _nextPos;
|
||||
const ptrdiff_t _end;
|
||||
til::rectangle _run;
|
||||
|
||||
void _calculateArea()
|
||||
{
|
||||
// Backup the position as the next one.
|
||||
_nextPos = _pos;
|
||||
|
||||
// Seek forward until we find an on bit.
|
||||
while (_nextPos < _end && !_values[_nextPos])
|
||||
{
|
||||
++_nextPos;
|
||||
}
|
||||
|
||||
// If we haven't reached the end yet...
|
||||
if (_nextPos < _end)
|
||||
{
|
||||
// pos is now at the first on bit.
|
||||
const auto runStart = _rc.point_at(_nextPos);
|
||||
|
||||
// We'll only count up until the end of this row.
|
||||
// a run can be a max of one row tall.
|
||||
const ptrdiff_t rowEndIndex = _rc.index_of(til::point(_rc.right() - 1, runStart.y())) + 1;
|
||||
|
||||
// Find the length for the rectangle.
|
||||
ptrdiff_t runLength = 0;
|
||||
|
||||
// We have at least 1 so start with a do/while.
|
||||
do
|
||||
{
|
||||
++_nextPos;
|
||||
++runLength;
|
||||
} while (_nextPos < rowEndIndex && _values[_nextPos]);
|
||||
// Keep going until we reach end of row, end of the buffer, or the next bit is off.
|
||||
|
||||
// Assemble and store that run.
|
||||
_run = til::rectangle{ runStart, til::size{ runLength, static_cast<ptrdiff_t>(1) } };
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we reached the end, set the pos because the run is empty.
|
||||
_pos = _nextPos;
|
||||
_run = til::rectangle{};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class bitmap
|
||||
{
|
||||
public:
|
||||
using const_iterator = const const_bitterator;
|
||||
using const_iterator = details::_bitmap_const_iterator;
|
||||
|
||||
bitmap() :
|
||||
bitmap(0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
bitmap(size_t width, size_t height) :
|
||||
bitmap(til::size{ width, height })
|
||||
bitmap() noexcept :
|
||||
_sz{},
|
||||
_rc{},
|
||||
_bits{},
|
||||
_dirty{},
|
||||
_runs{}
|
||||
{
|
||||
}
|
||||
|
||||
bitmap(til::size sz) :
|
||||
_size(sz),
|
||||
_bits(sz.area(), true),
|
||||
_empty(false)
|
||||
bitmap(sz, false)
|
||||
{
|
||||
}
|
||||
|
||||
bitmap(til::size sz, bool fill) :
|
||||
_sz(sz),
|
||||
_rc(sz),
|
||||
_bits(_sz.area()),
|
||||
_dirty(fill ? sz : til::rectangle{}),
|
||||
_runs{}
|
||||
{
|
||||
if (fill)
|
||||
{
|
||||
set_all();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool operator==(const bitmap& other) const noexcept
|
||||
{
|
||||
return _sz == other._sz &&
|
||||
_rc == other._rc &&
|
||||
_dirty == other._dirty && // dirty is before bits because it's a rough estimate of bits and a faster comparison.
|
||||
_bits == other._bits;
|
||||
// _runs excluded because it's a cache of generated state.
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const bitmap& other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_bitterator(_bits, 0);
|
||||
return const_iterator(_bits, _sz, 0);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return const_bitterator(_bits, _size.area());
|
||||
return const_iterator(_bits, _sz, _sz.area());
|
||||
}
|
||||
|
||||
const_iterator begin_row(size_t row) const
|
||||
const std::vector<til::rectangle>& runs() const
|
||||
{
|
||||
return const_bitterator(_bits, row * _size.width());
|
||||
}
|
||||
|
||||
const_iterator end_row(size_t row) const
|
||||
{
|
||||
return const_bitterator(_bits, (row + 1) * _size.width());
|
||||
}
|
||||
|
||||
const_runerator begin_runs() const
|
||||
{
|
||||
return const_runerator(_bits, _size, 0);
|
||||
}
|
||||
|
||||
const_runerator end_runs() const
|
||||
{
|
||||
return const_runerator(_bits, _size, _size.area());
|
||||
}
|
||||
|
||||
void set(til::point pt)
|
||||
{
|
||||
_bits[pt.y() * _size.width() + pt.x()] = true;
|
||||
_empty = false;
|
||||
}
|
||||
|
||||
void reset(til::point pt)
|
||||
{
|
||||
_bits[pt.y() * _size.width() + pt.x()] = false;
|
||||
}
|
||||
|
||||
void set(til::rectangle rc)
|
||||
{
|
||||
for (auto pt : rc)
|
||||
// If we don't have cached runs, rebuild.
|
||||
if (!_runs.has_value())
|
||||
{
|
||||
set(pt);
|
||||
// If there's only one square dirty, quick save it off and be done.
|
||||
if (one())
|
||||
{
|
||||
_runs.emplace({ _dirty });
|
||||
}
|
||||
else
|
||||
{
|
||||
_runs.emplace(begin(), end());
|
||||
}
|
||||
}
|
||||
|
||||
// Return a reference to the runs.
|
||||
return _runs.value();
|
||||
}
|
||||
|
||||
// optional fill the uncovered area with bits.
|
||||
void translate(const til::point delta, bool fill = false)
|
||||
{
|
||||
// FUTURE: PERF: GH #4015: This could use in-place walk semantics instead of a temporary.
|
||||
til::bitmap other{ _sz };
|
||||
|
||||
for (auto run : *this)
|
||||
{
|
||||
// Offset by the delta
|
||||
run += delta;
|
||||
|
||||
// Intersect with the bounds of our bitmap area
|
||||
// as part of it could have slid out of bounds.
|
||||
run &= _rc;
|
||||
|
||||
// Set it into the new bitmap.
|
||||
other.set(run);
|
||||
}
|
||||
|
||||
// If we were asked to fill... find the uncovered region.
|
||||
if (fill)
|
||||
{
|
||||
// Original Rect of As.
|
||||
//
|
||||
// X <-- origin
|
||||
// A A A A
|
||||
// A A A A
|
||||
// A A A A
|
||||
// A A A A
|
||||
const auto originalRect = _rc;
|
||||
|
||||
// If Delta = (2, 2)
|
||||
// Translated Rect of Bs.
|
||||
//
|
||||
// X <-- origin
|
||||
//
|
||||
//
|
||||
// B B B B
|
||||
// B B B B
|
||||
// B B B B
|
||||
// B B B B
|
||||
const auto translatedRect = _rc + delta;
|
||||
|
||||
// Subtract the B from the A one to see what wasn't filled by the move.
|
||||
// C is the overlap of A and B:
|
||||
//
|
||||
// X <-- origin
|
||||
// A A A A 1 1 1 1
|
||||
// A A A A 1 1 1 1
|
||||
// A A C C B B subtract 2 2
|
||||
// A A C C B B ---------> 2 2
|
||||
// B B B B A - B
|
||||
// B B B B
|
||||
//
|
||||
// 1 and 2 are the spaces to fill that are "uncovered".
|
||||
const auto fillRects = originalRect - translatedRect;
|
||||
for (const auto& f : fillRects)
|
||||
{
|
||||
other.set(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Swap us with the temporary one.
|
||||
std::swap(other, *this);
|
||||
}
|
||||
|
||||
void set(const til::point pt)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, !_rc.contains(pt));
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
til::at(_bits, _rc.index_of(pt)) = true;
|
||||
|
||||
_dirty |= til::rectangle{ pt };
|
||||
}
|
||||
|
||||
void set(const til::rectangle rc)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, !_rc.contains(rc));
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
for (const auto pt : rc)
|
||||
{
|
||||
til::at(_bits, _rc.index_of(pt)) = true;
|
||||
}
|
||||
|
||||
_dirty |= rc;
|
||||
}
|
||||
|
||||
void set_all() noexcept
|
||||
{
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
_bits.set();
|
||||
_dirty = _rc;
|
||||
}
|
||||
|
||||
void reset_all() noexcept
|
||||
{
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
_bits.reset();
|
||||
_dirty = {};
|
||||
}
|
||||
|
||||
// True if we resized. False if it was the same size as before.
|
||||
// Set fill if you want the new region (on growing) to be marked dirty.
|
||||
bool resize(til::size size, bool fill = false)
|
||||
{
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
// Don't resize if it's not different
|
||||
if (_sz != size)
|
||||
{
|
||||
// Make a new bitmap for the other side, empty initially.
|
||||
auto newMap = bitmap(size, false);
|
||||
|
||||
// Copy any regions that overlap from this map to the new one.
|
||||
// Just iterate our runs...
|
||||
for (const auto run : *this)
|
||||
{
|
||||
// intersect them with the new map
|
||||
// so we don't attempt to set bits that fit outside
|
||||
// the new one.
|
||||
const auto intersect = run & newMap._rc;
|
||||
|
||||
// and if there is still anything left, set them.
|
||||
if (!intersect.empty())
|
||||
{
|
||||
newMap.set(intersect);
|
||||
}
|
||||
}
|
||||
|
||||
// Then, if we were requested to fill the new space on growing,
|
||||
// find the space in the new rectangle that wasn't in the old
|
||||
// and fill it up.
|
||||
if (fill)
|
||||
{
|
||||
// A subtraction will yield anything in the new that isn't
|
||||
// a part of the old.
|
||||
const auto newAreas = newMap._rc - _rc;
|
||||
for (const auto& area : newAreas)
|
||||
{
|
||||
newMap.set(area);
|
||||
}
|
||||
}
|
||||
|
||||
// Swap and return.
|
||||
std::swap(newMap, *this);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void reset(til::rectangle rc)
|
||||
bool one() const
|
||||
{
|
||||
for (auto pt : rc)
|
||||
return _dirty.size() == til::size{ 1, 1 };
|
||||
}
|
||||
|
||||
constexpr bool any() const noexcept
|
||||
{
|
||||
return !none();
|
||||
}
|
||||
|
||||
constexpr bool none() const noexcept
|
||||
{
|
||||
return _dirty.empty();
|
||||
}
|
||||
|
||||
constexpr bool all() const noexcept
|
||||
{
|
||||
return _dirty == _rc;
|
||||
}
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
std::wstringstream wss;
|
||||
wss << std::endl
|
||||
<< L"Bitmap of size " << _sz.to_string() << " contains the following dirty regions:" << std::endl;
|
||||
wss << L"Runs:" << std::endl;
|
||||
|
||||
for (auto& item : *this)
|
||||
{
|
||||
reset(pt);
|
||||
}
|
||||
}
|
||||
|
||||
void set_all()
|
||||
{
|
||||
// .clear() then .resize(_size(), true) throws an assert (unsupported operation)
|
||||
// .assign(_size(), true) throws an assert (unsupported operation)
|
||||
|
||||
set(til::rectangle{ til::point{ 0, 0 }, _size });
|
||||
}
|
||||
|
||||
void reset_all()
|
||||
{
|
||||
// .clear() then .resize(_size(), false) throws an assert (unsupported operation)
|
||||
// .assign(_size(), false) throws an assert (unsupported operation)
|
||||
reset(til::rectangle{ til::point{ 0, 0 }, _size });
|
||||
_empty = true;
|
||||
}
|
||||
|
||||
void resize(til::size size)
|
||||
{
|
||||
// Don't resize if it's not different as we mark the whole thing dirty on resize.
|
||||
// TODO: marking it dirty might not be necessary or we should be smart about it
|
||||
// (mark none of it dirty on resize down, mark just the edges on up?)
|
||||
if (_size != size)
|
||||
{
|
||||
_size = size;
|
||||
// .resize(_size(), true) throws an assert (unsupported operation)
|
||||
_bits = std::vector<bool>(_size.area(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void resize(size_t width, size_t height)
|
||||
{
|
||||
resize(til::size{ width, height });
|
||||
}
|
||||
|
||||
constexpr bool empty() const
|
||||
{
|
||||
return _empty;
|
||||
}
|
||||
|
||||
const til::size& size() const
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return !_bits.empty();
|
||||
}
|
||||
|
||||
bitmap operator+(const point& pt) const
|
||||
{
|
||||
auto temp = *this;
|
||||
return temp += pt;
|
||||
}
|
||||
|
||||
bitmap& operator+=(const point& pt)
|
||||
{
|
||||
// early return if nothing to do.
|
||||
if (pt.x() == 0 && pt.y() == 0)
|
||||
{
|
||||
return (*this);
|
||||
wss << L"\t- " << item.to_string() << std::endl;
|
||||
}
|
||||
|
||||
// If we're told to shift the whole thing by an entire width or height,
|
||||
// the effect is to just clear the whole bitmap.
|
||||
if (pt.x() >= _size.width() || pt.y() >= _size.height())
|
||||
{
|
||||
reset_all();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// TODO: any way to reconcile this with walk directions from scrolling apis?
|
||||
// TODO: actually implement translation.
|
||||
|
||||
return (*this);
|
||||
return wss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
til::rectangle _dirty;
|
||||
til::size _sz;
|
||||
til::rectangle _rc;
|
||||
dynamic_bitset<> _bits;
|
||||
|
||||
mutable std::optional<std::vector<til::rectangle>> _runs;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::BitmapTests;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool _empty;
|
||||
til::size _size;
|
||||
std::vector<bool> _bits;
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef __WEX_COMMON_H__
|
||||
namespace WEX::TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<::til::bitmap>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::bitmap& rect)
|
||||
{
|
||||
return WEX::Common::NoThrowString(rect.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<::til::bitmap, ::til::bitmap>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const ::til::bitmap& expected, const ::til::bitmap& actual) noexcept
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
static bool AreSame(const ::til::bitmap& expected, const ::til::bitmap& actual) noexcept
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const ::til::bitmap& expectedLess, const ::til::bitmap& expectedGreater) = delete;
|
||||
|
||||
static bool IsGreaterThan(const ::til::bitmap& expectedGreater, const ::til::bitmap& expectedLess) = delete;
|
||||
|
||||
static bool IsNull(const ::til::bitmap& object) noexcept
|
||||
{
|
||||
return object == til::bitmap{};
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class BitteratorTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
};
|
||||
@@ -7,210 +7,11 @@
|
||||
#include "size.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
#define _TIL_INLINEPREFIX __declspec(noinline) inline
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
// RECTANGLE VS SIZE
|
||||
// ADD will grow the total area of the rectangle. The sign is the direction to grow.
|
||||
_TIL_INLINEPREFIX rectangle operator+(const rectangle& lhs, const size& rhs)
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = lhs.left();
|
||||
auto r = lhs.right();
|
||||
auto t = lhs.top();
|
||||
auto b = lhs.bottom();
|
||||
// Operators go here when they involve two headers that can't/don't include each other.
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = rhs.width();
|
||||
const auto height = rhs.height();
|
||||
|
||||
// Since this is the add operation versus a size, the result
|
||||
// should grow the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because right stretches outward (to the right).
|
||||
//
|
||||
// Example with adding width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because left stretches outward (to the left).
|
||||
//
|
||||
// Example with adding width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| |--x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because bottom stretches outward (to the down).
|
||||
//
|
||||
// Example with adding height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| | |
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because top stretches outward (to the up).
|
||||
//
|
||||
// Example with adding height -2...
|
||||
// |-- x = origin
|
||||
// |
|
||||
// | |---------|
|
||||
// V | |
|
||||
// x---------| x |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX rectangle& operator+=(rectangle& lhs, const size& rhs)
|
||||
{
|
||||
lhs = lhs + rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// SUB will shrink the total area of the rectangle. The sign is the direction to shrink.
|
||||
_TIL_INLINEPREFIX rectangle operator-(const rectangle& lhs, const size& rhs)
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = lhs.left();
|
||||
auto r = lhs.right();
|
||||
auto t = lhs.top();
|
||||
auto b = lhs.bottom();
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = rhs.width();
|
||||
const auto height = rhs.height();
|
||||
|
||||
// Since this is the subtract operation versus a size, the result
|
||||
// should shrink the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because right pulls inward (to the left).
|
||||
//
|
||||
// Example with subtracting width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the negative makes the rectangle "shrink"
|
||||
// because left pulls inward (to the right).
|
||||
//
|
||||
// Example with subtracting width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x |------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because bottom pulls inward (to the up).
|
||||
//
|
||||
// Example with subtracting height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | |---------|
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because top pulls inward (to the down).
|
||||
//
|
||||
// Example with subtracting height -2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x
|
||||
// | |
|
||||
// | | |---------|
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX rectangle& operator-=(rectangle& lhs, const size& rhs)
|
||||
{
|
||||
lhs = lhs - rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
// MUL will scale the entire rectangle by the size L/R * WIDTH and T/B * HEIGHT.
|
||||
_TIL_INLINEPREFIX rectangle operator*(const rectangle& lhs, const size& rhs)
|
||||
{
|
||||
ptrdiff_t l;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.left()) * rhs.width()).AssignIfValid(&l));
|
||||
|
||||
ptrdiff_t t;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.top()) * rhs.height()).AssignIfValid(&t));
|
||||
|
||||
ptrdiff_t r;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.right()) * rhs.width()).AssignIfValid(&r));
|
||||
|
||||
ptrdiff_t b;
|
||||
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.bottom()) * rhs.height()).AssignIfValid(&b));
|
||||
|
||||
return til::rectangle{ l, t, r, b };
|
||||
}
|
||||
|
||||
// POINT VS SIZE
|
||||
#pragma region POINT VS SIZE
|
||||
// This is a convenience and will take X vs WIDTH and Y vs HEIGHT.
|
||||
_TIL_INLINEPREFIX point operator+(const point& lhs, const size& rhs)
|
||||
{
|
||||
@@ -231,8 +32,9 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
return lhs / til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
// SIZE VS POINT
|
||||
#pragma region SIZE VS POINT
|
||||
// This is a convenience and will take WIDTH vs X and HEIGHT vs Y.
|
||||
_TIL_INLINEPREFIX size operator+(const size& lhs, const point& rhs)
|
||||
{
|
||||
@@ -253,4 +55,5 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
return lhs / til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
#pragma endregion
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to point from anything that has an X and a Y field that appear convertible to an integer value
|
||||
// This template will convert to size from anything that has an X and a Y field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
|
||||
point(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to point from anything that has a x and a y field that appear convertible to an integer value
|
||||
// This template will convert to size from anything that has a x and a y field that appear convertible to an integer value
|
||||
template<typename TOther>
|
||||
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().x)> && std::is_integral_v<decltype(std::declval<TOther>().y)>, int> /*sentinel*/ = 0) :
|
||||
point(static_cast<ptrdiff_t>(other.x), static_cast<ptrdiff_t>(other.y))
|
||||
@@ -64,11 +64,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept
|
||||
{
|
||||
return _x != 0 || _y != 0;
|
||||
}
|
||||
|
||||
constexpr bool operator<(const point& other) const noexcept
|
||||
{
|
||||
if (_y < other._y)
|
||||
@@ -198,6 +193,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
}
|
||||
#endif
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
return wil::str_printf<std::wstring>(L"(X:%td, Y:%td)", x(), y());
|
||||
}
|
||||
|
||||
protected:
|
||||
ptrdiff_t _x;
|
||||
ptrdiff_t _y;
|
||||
@@ -217,7 +217,7 @@ namespace WEX::TestExecution
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::point& point)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"(X:%td, Y:%td)", point.x(), point.y());
|
||||
return WEX::Common::NoThrowString(point.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -7,18 +7,95 @@
|
||||
#include "size.h"
|
||||
#include "some.h"
|
||||
|
||||
#include "recterator.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class RectangleTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
class _rectangle_const_iterator
|
||||
{
|
||||
public:
|
||||
constexpr _rectangle_const_iterator(point topLeft, point bottomRight) :
|
||||
_topLeft(topLeft),
|
||||
_bottomRight(bottomRight),
|
||||
_current(topLeft)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr _rectangle_const_iterator(point topLeft, point bottomRight, point start) :
|
||||
_topLeft(topLeft),
|
||||
_bottomRight(bottomRight),
|
||||
_current(start)
|
||||
{
|
||||
}
|
||||
|
||||
_rectangle_const_iterator& operator++()
|
||||
{
|
||||
ptrdiff_t nextX;
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(_current.x(), 1).AssignIfValid(&nextX));
|
||||
|
||||
if (nextX >= _bottomRight.x())
|
||||
{
|
||||
ptrdiff_t nextY;
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(_current.y(), 1).AssignIfValid(&nextY));
|
||||
// Note for the standard Left-to-Right, Top-to-Bottom walk,
|
||||
// the end position is one cell below the bottom left.
|
||||
// (or more accurately, on the exclusive bottom line in the inclusive left column.)
|
||||
_current = { _topLeft.x(), nextY };
|
||||
}
|
||||
else
|
||||
{
|
||||
_current = { nextX, _current.y() };
|
||||
}
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const _rectangle_const_iterator& other) const
|
||||
{
|
||||
return _current == other._current &&
|
||||
_topLeft == other._topLeft &&
|
||||
_bottomRight == other._bottomRight;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const _rectangle_const_iterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr bool operator<(const _rectangle_const_iterator& other) const
|
||||
{
|
||||
return _current < other._current;
|
||||
}
|
||||
|
||||
constexpr bool operator>(const _rectangle_const_iterator& other) const
|
||||
{
|
||||
return _current > other._current;
|
||||
}
|
||||
|
||||
constexpr point operator*() const
|
||||
{
|
||||
return _current;
|
||||
}
|
||||
|
||||
protected:
|
||||
point _current;
|
||||
const point _topLeft;
|
||||
const point _bottomRight;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::RectangleTests;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
class rectangle
|
||||
{
|
||||
public:
|
||||
using const_iterator = recterator;
|
||||
using const_iterator = details::_rectangle_const_iterator;
|
||||
|
||||
constexpr rectangle() noexcept :
|
||||
rectangle(til::point{ 0, 0 }, til::point{ 0, 0 })
|
||||
@@ -102,13 +179,6 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
}
|
||||
|
||||
constexpr rectangle& operator=(const rectangle other) noexcept
|
||||
{
|
||||
_topLeft = other._topLeft;
|
||||
_bottomRight = other._bottomRight;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const rectangle& other) const noexcept
|
||||
{
|
||||
return _topLeft == other._topLeft &&
|
||||
@@ -120,22 +190,29 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept
|
||||
explicit constexpr operator bool() const noexcept
|
||||
{
|
||||
return _topLeft.x() < _bottomRight.x() &&
|
||||
_topLeft.y() < _bottomRight.y();
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
constexpr const_iterator begin() const
|
||||
{
|
||||
return recterator(_topLeft, size());
|
||||
return const_iterator(_topLeft, _bottomRight);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
constexpr const_iterator end() const
|
||||
{
|
||||
return recterator(_topLeft, size(), { _topLeft.x(), _topLeft.y() + height() });
|
||||
// For the standard walk: Left-To-Right then Top-To-Bottom
|
||||
// the end box is one cell below the left most column.
|
||||
// |----| 5x2 square. Remember bottom & right are exclusive
|
||||
// | | while top & left are inclusive.
|
||||
// X----- X is the end position.
|
||||
|
||||
return const_iterator(_topLeft, _bottomRight, { _topLeft.x(), _bottomRight.y() });
|
||||
}
|
||||
|
||||
#pragma region RECTANGLE OPERATORS
|
||||
// OR = union
|
||||
constexpr rectangle operator|(const rectangle& other) const noexcept
|
||||
{
|
||||
@@ -168,6 +245,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return rectangle{ l, t, r, b };
|
||||
}
|
||||
|
||||
constexpr rectangle& operator|=(const rectangle& other) noexcept
|
||||
{
|
||||
*this = *this | other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// AND = intersect
|
||||
constexpr rectangle operator&(const rectangle& other) const noexcept
|
||||
{
|
||||
@@ -192,6 +275,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return rectangle{ l, t, r, b };
|
||||
}
|
||||
|
||||
constexpr rectangle& operator&=(const rectangle& other) noexcept
|
||||
{
|
||||
*this = *this & other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// - = subtract
|
||||
some<rectangle, 4> operator-(const rectangle& other) const
|
||||
{
|
||||
@@ -323,6 +412,231 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
|
||||
return result;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RECTANGLE VS POINT
|
||||
// ADD will translate (offset) the rectangle by the point.
|
||||
rectangle operator+(const point& point) const
|
||||
{
|
||||
ptrdiff_t l, t, r, b;
|
||||
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(left(), point.x()).AssignIfValid(&l));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(top(), point.y()).AssignIfValid(&t));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(right(), point.x()).AssignIfValid(&r));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(bottom(), point.y()).AssignIfValid(&b));
|
||||
|
||||
return til::rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator+=(const point& point)
|
||||
{
|
||||
*this = *this + point;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// SUB will translate (offset) the rectangle by the point.
|
||||
rectangle operator-(const point& point) const
|
||||
{
|
||||
ptrdiff_t l, t, r, b;
|
||||
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(left(), point.x()).AssignIfValid(&l));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(top(), point.y()).AssignIfValid(&t));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(right(), point.x()).AssignIfValid(&r));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(bottom(), point.y()).AssignIfValid(&b));
|
||||
|
||||
return til::rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator-=(const point& point)
|
||||
{
|
||||
*this = *this - point;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RECTANGLE VS SIZE
|
||||
// ADD will grow the total area of the rectangle. The sign is the direction to grow.
|
||||
rectangle operator+(const size& size) const
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = left();
|
||||
auto r = right();
|
||||
auto t = top();
|
||||
auto b = bottom();
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = size.width();
|
||||
const auto height = size.height();
|
||||
|
||||
// Since this is the add operation versus a size, the result
|
||||
// should grow the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because right stretches outward (to the right).
|
||||
//
|
||||
// Example with adding width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because left stretches outward (to the left).
|
||||
//
|
||||
// Example with adding width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| |--x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because bottom stretches outward (to the down).
|
||||
//
|
||||
// Example with adding height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| | |
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because top stretches outward (to the up).
|
||||
//
|
||||
// Example with adding height -2...
|
||||
// |-- x = origin
|
||||
// |
|
||||
// | |---------|
|
||||
// V | |
|
||||
// x---------| x |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator+=(const size& size)
|
||||
{
|
||||
*this = *this + size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// SUB will shrink the total area of the rectangle. The sign is the direction to shrink.
|
||||
rectangle operator-(const size& size) const
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = left();
|
||||
auto r = right();
|
||||
auto t = top();
|
||||
auto b = bottom();
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = size.width();
|
||||
const auto height = size.height();
|
||||
|
||||
// Since this is the subtract operation versus a size, the result
|
||||
// should shrink the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because right pulls inward (to the left).
|
||||
//
|
||||
// Example with subtracting width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the negative makes the rectangle "shrink"
|
||||
// because left pulls inward (to the right).
|
||||
//
|
||||
// Example with subtracting width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x |------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because bottom pulls inward (to the up).
|
||||
//
|
||||
// Example with subtracting height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | |---------|
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because top pulls inward (to the down).
|
||||
//
|
||||
// Example with subtracting height -2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x
|
||||
// | |
|
||||
// | | |---------|
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator-=(const size& size)
|
||||
{
|
||||
*this = *this - size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
constexpr ptrdiff_t top() const noexcept
|
||||
{
|
||||
@@ -421,6 +735,57 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return !operator bool();
|
||||
}
|
||||
|
||||
constexpr bool contains(til::point pt) const
|
||||
{
|
||||
return pt.x() >= _topLeft.x() && pt.x() < _bottomRight.x() &&
|
||||
pt.y() >= _topLeft.y() && pt.y() < _bottomRight.y();
|
||||
}
|
||||
|
||||
bool contains(ptrdiff_t index) const
|
||||
{
|
||||
return index >= 0 && index < size().area();
|
||||
}
|
||||
|
||||
constexpr bool contains(til::rectangle rc) const
|
||||
{
|
||||
// Union the other rectangle and ourselves.
|
||||
// If the result of that didn't grow at all, then we already
|
||||
// fully contained the rectangle we were given.
|
||||
return (*this | rc) == *this;
|
||||
}
|
||||
|
||||
ptrdiff_t index_of(til::point pt) const
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, !contains(pt));
|
||||
|
||||
// Take Y away from the top to find how many rows down
|
||||
auto check = base::CheckSub(pt.y(), top());
|
||||
|
||||
// Multiply by the width because we've passed that many
|
||||
// widths-worth of indices.
|
||||
check *= width();
|
||||
|
||||
// Then add in the last few indices in the x position this row
|
||||
// and subtract left to find the offset from left edge.
|
||||
check = check + pt.x() - left();
|
||||
|
||||
ptrdiff_t result;
|
||||
THROW_HR_IF(E_ABORT, !check.AssignIfValid(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
til::point point_at(ptrdiff_t index) const
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, !contains(index));
|
||||
|
||||
const auto div = std::div(index, width());
|
||||
|
||||
// Not checking math on these because we're presuming
|
||||
// that the point can't be in bounds of a rectangle where
|
||||
// this would overflow on addition after the division.
|
||||
return til::point{ div.rem + left(), div.quot + top() };
|
||||
}
|
||||
|
||||
#ifdef _WINCONTYPES_
|
||||
// NOTE: This will convert back to INCLUSIVE on the way out because
|
||||
// that is generally how SMALL_RECTs are handled in console code and via the APIs.
|
||||
@@ -454,6 +819,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
}
|
||||
#endif
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
return wil::str_printf<std::wstring>(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", left(), top(), right(), bottom(), width(), height());
|
||||
}
|
||||
|
||||
protected:
|
||||
til::point _topLeft;
|
||||
til::point _bottomRight;
|
||||
@@ -473,7 +843,7 @@ namespace WEX::TestExecution
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::rectangle& rect)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", rect.left(), rect.top(), rect.right(), rect.bottom(), rect.width(), rect.height());
|
||||
return WEX::Common::NoThrowString(rect.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "point.h"
|
||||
#include "size.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class RecteratorTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
class recterator
|
||||
{
|
||||
public:
|
||||
recterator(point topLeft, size size) :
|
||||
_topLeft(topLeft),
|
||||
_size(size),
|
||||
_current(topLeft)
|
||||
{
|
||||
}
|
||||
|
||||
recterator(point topLeft, size size, point start) :
|
||||
_topLeft(topLeft),
|
||||
_size(size),
|
||||
_current(start)
|
||||
{
|
||||
}
|
||||
|
||||
recterator& operator++()
|
||||
{
|
||||
if (_current.x() + 1 >= _topLeft.x() + _size.width())
|
||||
{
|
||||
_current = { _topLeft.x(), _current.y() + 1 };
|
||||
}
|
||||
else
|
||||
{
|
||||
_current = { _current.x() + 1, _current.y() };
|
||||
}
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
bool operator==(const recterator& other) const
|
||||
{
|
||||
return _current == other._current &&
|
||||
_topLeft == other._topLeft &&
|
||||
_size == other._size;
|
||||
}
|
||||
|
||||
bool operator!=(const recterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const recterator& other) const
|
||||
{
|
||||
return _current < other._current;
|
||||
}
|
||||
|
||||
bool operator>(const recterator& other) const
|
||||
{
|
||||
return _current > other._current;
|
||||
}
|
||||
|
||||
point operator*() const
|
||||
{
|
||||
return _current;
|
||||
}
|
||||
|
||||
protected:
|
||||
point _current;
|
||||
const point _topLeft;
|
||||
const size _size;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::RecteratorTests;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef __WEX_COMMON_H__
|
||||
namespace WEX::TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<::til::recterator>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::recterator& /*rect*/)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"Yep that's a recterator.");
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<::til::recterator, ::til::recterator>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const ::til::recterator& expected, const ::til::recterator& actual) noexcept
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
static bool AreSame(const ::til::recterator& expected, const ::til::recterator& actual) noexcept
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const ::til::recterator& expectedLess, const ::til::recterator& expectedGreater) = delete;
|
||||
|
||||
static bool IsGreaterThan(const ::til::recterator& expectedGreater, const ::til::recterator& expectedLess) = delete;
|
||||
|
||||
static bool IsNull(const ::til::recterator& object) noexcept = delete;
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
@@ -64,9 +64,9 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr operator bool() const noexcept
|
||||
constexpr explicit operator bool() const noexcept
|
||||
{
|
||||
return _width != 0 || _height != 0;
|
||||
return _width > 0 && _height > 0;
|
||||
}
|
||||
|
||||
size operator+(const size& other) const
|
||||
@@ -220,6 +220,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
}
|
||||
#endif
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
return wil::str_printf<std::wstring>(L"[W:%td, H:%td]", width(), height());
|
||||
}
|
||||
|
||||
protected:
|
||||
ptrdiff_t _width;
|
||||
ptrdiff_t _height;
|
||||
@@ -239,7 +244,7 @@ namespace WEX::TestExecution
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::size& size)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"[W:%td, H:%td]", size.width(), size.height());
|
||||
return WEX::Common::NoThrowString(size.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -127,6 +127,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
return !_used;
|
||||
}
|
||||
|
||||
constexpr void clear() noexcept
|
||||
{
|
||||
_used = 0;
|
||||
_array = {}; // should free members, if necessary.
|
||||
}
|
||||
|
||||
constexpr const_reference at(size_type pos) const
|
||||
{
|
||||
if (_used <= pos)
|
||||
@@ -169,6 +175,18 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
++_used;
|
||||
}
|
||||
|
||||
void push_back(T&& val)
|
||||
{
|
||||
if (_used >= N)
|
||||
{
|
||||
_outOfRange();
|
||||
}
|
||||
|
||||
til::at(_array, _used) = std::move(val);
|
||||
|
||||
++_used;
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
if (_used <= 0)
|
||||
@@ -190,6 +208,21 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
throw std::out_of_range("invalid some<T, N> subscript");
|
||||
}
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
std::wstringstream wss;
|
||||
wss << std::endl
|
||||
<< L"Some contains " << size() << " of max size " << max_size() << ":" << std::endl;
|
||||
wss << L"Elements:" << std::endl;
|
||||
|
||||
for (auto& item : *this)
|
||||
{
|
||||
wss << L"\t- " << item.to_string() << std::endl;
|
||||
}
|
||||
|
||||
return wss.str();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -202,15 +235,7 @@ namespace WEX::TestExecution
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::some<T, N>& some)
|
||||
{
|
||||
auto str = WEX::Common::NoThrowString().Format(L"\r\nSome contains %d of max size %d:\r\nElements:\r\n", some.size(), some.max_size());
|
||||
|
||||
for (auto& item : some)
|
||||
{
|
||||
const auto itemStr = WEX::TestExecution::VerifyOutputTraits<T>::ToString(item);
|
||||
str.AppendFormat(L"\t- %ws\r\n", (const wchar_t*)itemStr);
|
||||
}
|
||||
|
||||
return str;
|
||||
return WEX::Common::NoThrowString(some.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user