mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-07 06:39:44 +00:00
Compare commits
5 Commits
dev/migrie
...
dev/miniks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eb08ff804 | ||
|
|
65ada350ce | ||
|
|
3fb8a8be4b | ||
|
|
cd1c4b6b10 | ||
|
|
6cac0969f3 |
@@ -1,25 +1,57 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Microsoft
|
||||
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AlignConsecutiveMacros: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
#AllowShortLambdasOnASingleLine: Inline
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: true
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
ColumnLimit: 0
|
||||
CommentPragmas: "suppress"
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: false
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
FixNamespaceComments: false
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
@@ -31,13 +63,35 @@ IncludeCategories:
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: "BEGIN_TEST_METHOD_PROPERTIES|BEGIN_MODULE|BEGIN_TEST_CLASS|BEGIN_TEST_METHOD"
|
||||
MacroBlockEnd: "END_TEST_METHOD_PROPERTIES|END_MODULE|END_TEST_CLASS|END_TEST_METHOD"
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Left
|
||||
ReflowComments: false
|
||||
SortIncludes: false
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Latest
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
|
||||
4
.github/actions/spelling/allow/allow.txt
vendored
4
.github/actions/spelling/allow/allow.txt
vendored
@@ -78,13 +78,10 @@ ok'd
|
||||
overlined
|
||||
pipeline
|
||||
postmodern
|
||||
Powerline
|
||||
powerline
|
||||
ptys
|
||||
qof
|
||||
qps
|
||||
rclt
|
||||
regexes
|
||||
reimplementation
|
||||
reserialization
|
||||
reserialize
|
||||
@@ -103,7 +100,6 @@ TLDR
|
||||
tokenizes
|
||||
tonos
|
||||
toolset
|
||||
truthiness
|
||||
tshe
|
||||
ubuntu
|
||||
uiatextrange
|
||||
|
||||
14
.github/actions/spelling/allow/apis.txt
vendored
14
.github/actions/spelling/allow/apis.txt
vendored
@@ -29,12 +29,12 @@ Dacl
|
||||
dataobject
|
||||
dcomp
|
||||
DERR
|
||||
delayimp
|
||||
dlldata
|
||||
DNE
|
||||
DONTADDTORECENT
|
||||
DWMSBT
|
||||
DWMWA
|
||||
DWMWA
|
||||
DWORDLONG
|
||||
endfor
|
||||
ENDSESSION
|
||||
@@ -55,8 +55,6 @@ GETMOUSEHOVERTIME
|
||||
Hashtable
|
||||
HIGHCONTRASTON
|
||||
HIGHCONTRASTW
|
||||
hinternet
|
||||
HINTERNET
|
||||
hotkeys
|
||||
href
|
||||
hrgn
|
||||
@@ -76,7 +74,6 @@ IConnection
|
||||
ICustom
|
||||
IDialog
|
||||
IDirect
|
||||
Idn
|
||||
IExplorer
|
||||
IFACEMETHOD
|
||||
IFile
|
||||
@@ -88,7 +85,6 @@ IObject
|
||||
iosfwd
|
||||
IPackage
|
||||
IPeasant
|
||||
isa
|
||||
ISetup
|
||||
isspace
|
||||
IStorage
|
||||
@@ -162,13 +158,11 @@ rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
RLO
|
||||
ROOTOWNER
|
||||
roundf
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
SEH
|
||||
semver
|
||||
serializer
|
||||
SETVERSION
|
||||
@@ -216,14 +210,10 @@ UOI
|
||||
UPDATEINIFILE
|
||||
userenv
|
||||
USEROBJECTFLAGS
|
||||
Vcpp
|
||||
Viewbox
|
||||
virtualalloc
|
||||
vsnwprintf
|
||||
wcsstr
|
||||
wcstoui
|
||||
WDJ
|
||||
winhttp
|
||||
winmain
|
||||
winsta
|
||||
winstamin
|
||||
@@ -231,7 +221,6 @@ wmemcmp
|
||||
wpc
|
||||
WSF
|
||||
wsregex
|
||||
WWH
|
||||
wwinmain
|
||||
xchg
|
||||
XDocument
|
||||
@@ -257,4 +246,3 @@ xtree
|
||||
xutility
|
||||
YIcon
|
||||
YMax
|
||||
zwstring
|
||||
|
||||
9
.github/actions/spelling/allow/microsoft.txt
vendored
9
.github/actions/spelling/allow/microsoft.txt
vendored
@@ -9,11 +9,9 @@ appxbundle
|
||||
appxerror
|
||||
appxmanifest
|
||||
ATL
|
||||
autoexec
|
||||
backplating
|
||||
bitmaps
|
||||
BOMs
|
||||
COMPUTERNAME
|
||||
CPLs
|
||||
cpptools
|
||||
cppvsdbg
|
||||
@@ -28,7 +26,6 @@ dotnetfeed
|
||||
DTDs
|
||||
DWINRT
|
||||
enablewttlogging
|
||||
HOMESHARE
|
||||
Intelli
|
||||
IVisual
|
||||
libucrt
|
||||
@@ -36,7 +33,6 @@ libucrtd
|
||||
LKG
|
||||
LOCKFILE
|
||||
Lxss
|
||||
makepri
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
@@ -54,19 +50,15 @@ pgo
|
||||
pgosweep
|
||||
powerrename
|
||||
powershell
|
||||
priconfig
|
||||
PRIINFO
|
||||
propkey
|
||||
pscustomobject
|
||||
QWORD
|
||||
regedit
|
||||
resfiles
|
||||
robocopy
|
||||
SACLs
|
||||
segoe
|
||||
sdkddkver
|
||||
Shobjidl
|
||||
sid
|
||||
Skype
|
||||
SRW
|
||||
sxs
|
||||
@@ -79,7 +71,6 @@ tdbuildteamid
|
||||
ucrt
|
||||
ucrtd
|
||||
unvirtualized
|
||||
USERDNSDOMAIN
|
||||
VCRT
|
||||
vcruntime
|
||||
Virtualization
|
||||
|
||||
7
.github/actions/spelling/allow/names.txt
vendored
7
.github/actions/spelling/allow/names.txt
vendored
@@ -1,6 +1,6 @@
|
||||
Anup
|
||||
arkthur
|
||||
austdi
|
||||
arkthur
|
||||
Ballmer
|
||||
bhoj
|
||||
Bhojwani
|
||||
@@ -31,8 +31,8 @@ jerrysh
|
||||
Kaiyu
|
||||
kimwalisch
|
||||
KMehrain
|
||||
Kodelife
|
||||
KODELIFE
|
||||
Kodelife
|
||||
Kourosh
|
||||
kowalczyk
|
||||
leonardder
|
||||
@@ -61,7 +61,6 @@ oising
|
||||
oldnewthing
|
||||
opengl
|
||||
osgwiki
|
||||
Ottosson
|
||||
pabhojwa
|
||||
panos
|
||||
paulcam
|
||||
@@ -89,8 +88,8 @@ Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
zamora
|
||||
Zamora
|
||||
zamora
|
||||
zljubisic
|
||||
Zoey
|
||||
zorio
|
||||
|
||||
2
.github/actions/spelling/candidate.patterns
vendored
2
.github/actions/spelling/candidate.patterns
vendored
@@ -371,6 +371,8 @@ ipfs://[0-9a-z]*
|
||||
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
|
||||
# c99 hex digits (not the full format, just one I've seen)
|
||||
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
|
||||
# Punycode
|
||||
\bxn--[-0-9a-z]+
|
||||
# sha
|
||||
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
|
||||
3
.github/actions/spelling/excludes.txt
vendored
3
.github/actions/spelling/excludes.txt
vendored
@@ -106,14 +106,11 @@
|
||||
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
|
||||
^src/terminal/parser/ut_parser/Base64Test.cpp$
|
||||
^src/terminal/parser/ut_parser/run\.bat$
|
||||
^src/tools/benchcat
|
||||
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
|
||||
^src/tools/lnkd/lnkd\.bat$
|
||||
^src/tools/pixels/pixels\.bat$
|
||||
^src/tools/RenderingTests/main.cpp$
|
||||
^src/tools/texttests/fira\.txt$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ColorFix.cpp
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^tools/ReleaseEngineering/ServicingPipeline.ps1$
|
||||
ignore$
|
||||
|
||||
5
.github/actions/spelling/expect/alphabet.txt
vendored
5
.github/actions/spelling/expect/alphabet.txt
vendored
@@ -9,7 +9,6 @@ ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRS
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
ABCG
|
||||
ABE
|
||||
@@ -19,15 +18,11 @@ BBBBBBBB
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
efg
|
||||
EFG
|
||||
EFGh
|
||||
KLMNOQQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRS
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJPQRST
|
||||
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
|
||||
qrstuvwxyz
|
||||
qwerty
|
||||
|
||||
80
.github/actions/spelling/expect/expect.txt
vendored
80
.github/actions/spelling/expect/expect.txt
vendored
@@ -1,9 +1,7 @@
|
||||
aabbcc
|
||||
aarch
|
||||
ABANDONFONT
|
||||
abbcc
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXY
|
||||
ABCF
|
||||
abgr
|
||||
abi
|
||||
ABORTIFHUNG
|
||||
@@ -39,12 +37,10 @@ ansicpg
|
||||
ANSISYS
|
||||
ANSISYSRC
|
||||
ANSISYSSC
|
||||
answerback
|
||||
antialiasing
|
||||
ANull
|
||||
anycpu
|
||||
APARTMENTTHREADED
|
||||
APCA
|
||||
APCs
|
||||
APIENTRY
|
||||
apiset
|
||||
@@ -105,7 +101,6 @@ bcx
|
||||
bcz
|
||||
BEFOREPARENT
|
||||
beginthread
|
||||
benchcat
|
||||
bgfx
|
||||
bgidx
|
||||
Bgk
|
||||
@@ -159,6 +154,7 @@ capslock
|
||||
CARETBLINKINGENABLED
|
||||
CARRIAGERETURN
|
||||
cascadia
|
||||
castsi
|
||||
catid
|
||||
cazamor
|
||||
CBash
|
||||
@@ -179,6 +175,7 @@ CConsole
|
||||
CConversion
|
||||
CCRT
|
||||
cdd
|
||||
CDeclaration
|
||||
CEdit
|
||||
CELLSIZE
|
||||
cfae
|
||||
@@ -196,8 +193,7 @@ chh
|
||||
chk
|
||||
CHT
|
||||
Cic
|
||||
cielab
|
||||
Cielab
|
||||
CLA
|
||||
Clcompile
|
||||
CLE
|
||||
cleartype
|
||||
@@ -217,11 +213,11 @@ cmder
|
||||
CMDEXT
|
||||
cmh
|
||||
CMOUSEBUTTONS
|
||||
cmpeq
|
||||
cmt
|
||||
cmw
|
||||
cmyk
|
||||
CNL
|
||||
cnn
|
||||
cnt
|
||||
CNTRL
|
||||
Codeflow
|
||||
@@ -315,6 +311,7 @@ CPLINFO
|
||||
cplusplus
|
||||
CPPCORECHECK
|
||||
cppcorecheckrules
|
||||
cpprest
|
||||
cpprestsdk
|
||||
cppwinrt
|
||||
CProc
|
||||
@@ -335,7 +332,7 @@ Cspace
|
||||
csrmsg
|
||||
CSRSS
|
||||
csrutil
|
||||
CSTYLE
|
||||
cstyle
|
||||
CSwitch
|
||||
CTerminal
|
||||
CText
|
||||
@@ -410,26 +407,20 @@ DECANM
|
||||
DECARM
|
||||
DECAUPSS
|
||||
DECAWM
|
||||
DECBI
|
||||
DECBKM
|
||||
DECCARA
|
||||
DECCIR
|
||||
DECCKM
|
||||
DECCKSR
|
||||
DECCOLM
|
||||
DECCRA
|
||||
DECCTR
|
||||
DECDC
|
||||
DECDHL
|
||||
decdld
|
||||
DECDMAC
|
||||
DECDWL
|
||||
DECECM
|
||||
DECEKBD
|
||||
DECERA
|
||||
DECFI
|
||||
DECFRA
|
||||
DECIC
|
||||
DECID
|
||||
DECINVM
|
||||
DECKPAM
|
||||
@@ -440,7 +431,6 @@ DECMSR
|
||||
DECNKM
|
||||
DECNRCM
|
||||
DECOM
|
||||
decommit
|
||||
DECPCTERM
|
||||
DECPS
|
||||
DECRARA
|
||||
@@ -448,12 +438,9 @@ DECRC
|
||||
DECREQTPARM
|
||||
DECRLM
|
||||
DECRPM
|
||||
DECRQCRA
|
||||
DECRQM
|
||||
DECRQPSR
|
||||
DECRQSS
|
||||
DECRQTSR
|
||||
DECRSPS
|
||||
decrst
|
||||
DECSACE
|
||||
DECSASD
|
||||
@@ -474,7 +461,6 @@ DECSTBM
|
||||
DECSTGLT
|
||||
DECSTR
|
||||
DECSWL
|
||||
DECTABSR
|
||||
DECTCEM
|
||||
DECXCPR
|
||||
DEFAPP
|
||||
@@ -494,6 +480,7 @@ defterm
|
||||
DELAYLOAD
|
||||
DELETEONRELEASE
|
||||
Delt
|
||||
demoable
|
||||
depersist
|
||||
deprioritized
|
||||
deserializers
|
||||
@@ -550,6 +537,7 @@ DSSCL
|
||||
DSwap
|
||||
DTest
|
||||
DTTERM
|
||||
DUMMYUNIONNAME
|
||||
dup'ed
|
||||
dvi
|
||||
dwl
|
||||
@@ -577,7 +565,6 @@ EDITTEXT
|
||||
EDITUPDATE
|
||||
edputil
|
||||
Efast
|
||||
efghijklmn
|
||||
EHsc
|
||||
EINS
|
||||
EJO
|
||||
@@ -594,7 +581,6 @@ ENU
|
||||
ENUMLOGFONT
|
||||
ENUMLOGFONTEX
|
||||
enumranges
|
||||
EOK
|
||||
eplace
|
||||
EPres
|
||||
EQU
|
||||
@@ -604,6 +590,7 @@ ETW
|
||||
EUDC
|
||||
EVENTID
|
||||
eventing
|
||||
everytime
|
||||
evflags
|
||||
evt
|
||||
execd
|
||||
@@ -625,12 +612,8 @@ FACESIZE
|
||||
FAILIFTHERE
|
||||
fastlink
|
||||
fcharset
|
||||
FDEA
|
||||
fdw
|
||||
FECF
|
||||
FEEF
|
||||
fesb
|
||||
FFAF
|
||||
FFDE
|
||||
FFrom
|
||||
fgbg
|
||||
@@ -815,6 +798,7 @@ HIBYTE
|
||||
hicon
|
||||
HIDEWINDOW
|
||||
hinst
|
||||
Hirots
|
||||
HISTORYBUFS
|
||||
HISTORYNODUP
|
||||
HISTORYSIZE
|
||||
@@ -826,13 +810,9 @@ hkl
|
||||
HKLM
|
||||
hlocal
|
||||
hlsl
|
||||
HMB
|
||||
HMK
|
||||
hmod
|
||||
hmodule
|
||||
hmon
|
||||
homeglyphs
|
||||
homoglyph
|
||||
HORZ
|
||||
hostable
|
||||
hostlib
|
||||
@@ -919,6 +899,7 @@ INSERTMODE
|
||||
INTERACTIVITYBASE
|
||||
INTERCEPTCOPYPASTE
|
||||
INTERNALNAME
|
||||
inthread
|
||||
intsafe
|
||||
INVALIDARG
|
||||
INVALIDATERECT
|
||||
@@ -979,15 +960,13 @@ KLF
|
||||
KLMNO
|
||||
KLMNOPQRST
|
||||
KLMNOPQRSTQQQQQ
|
||||
KLMNOPQRSTUVWXY
|
||||
KLMNOPQRSTY
|
||||
KOK
|
||||
KPRIORITY
|
||||
KVM
|
||||
langid
|
||||
LANGUAGELIST
|
||||
lasterror
|
||||
LASTEXITCODE
|
||||
lastexitcode
|
||||
LAYOUTRTL
|
||||
lbl
|
||||
LBN
|
||||
@@ -1023,6 +1002,7 @@ lnkd
|
||||
lnkfile
|
||||
LNM
|
||||
LOADONCALL
|
||||
loadu
|
||||
LOBYTE
|
||||
localappdata
|
||||
locsrc
|
||||
@@ -1031,7 +1011,6 @@ LOGFONT
|
||||
LOGFONTA
|
||||
LOGFONTW
|
||||
logissue
|
||||
losslessly
|
||||
loword
|
||||
lparam
|
||||
LPCCH
|
||||
@@ -1139,12 +1118,10 @@ Mip
|
||||
MMBB
|
||||
mmcc
|
||||
MMCPL
|
||||
MMIX
|
||||
mmsystem
|
||||
MNC
|
||||
MNOPQ
|
||||
MNOPQR
|
||||
MNOPQRSTUVWXY
|
||||
MODALFRAME
|
||||
MODERNCORE
|
||||
MONITORINFO
|
||||
@@ -1154,6 +1131,7 @@ MOUSEACTIVATE
|
||||
MOUSEFIRST
|
||||
MOUSEHWHEEL
|
||||
MOUSEMOVE
|
||||
movemask
|
||||
MOVESTART
|
||||
msb
|
||||
msctf
|
||||
@@ -1278,17 +1256,20 @@ ntm
|
||||
nto
|
||||
ntrtl
|
||||
ntstatus
|
||||
ntsubauth
|
||||
NTSYSCALLAPI
|
||||
nttree
|
||||
nturtl
|
||||
ntuser
|
||||
NTVDM
|
||||
ntverp
|
||||
NTWIN
|
||||
nugetversions
|
||||
nullability
|
||||
nullness
|
||||
nullonfailure
|
||||
nullopts
|
||||
NULs
|
||||
numlock
|
||||
numpad
|
||||
NUMSCROLL
|
||||
@@ -1303,8 +1284,6 @@ OEMFONT
|
||||
OEMFORMAT
|
||||
OEMs
|
||||
offboarded
|
||||
oklab
|
||||
Oklab
|
||||
OLEAUT
|
||||
OLECHAR
|
||||
onecore
|
||||
@@ -1323,6 +1302,8 @@ opencode
|
||||
opencon
|
||||
openconsole
|
||||
openconsoleproxy
|
||||
OPENIF
|
||||
OPENLINK
|
||||
openps
|
||||
openvt
|
||||
ORIGINALFILENAME
|
||||
@@ -1375,7 +1356,9 @@ pcg
|
||||
pch
|
||||
PCIDLIST
|
||||
PCIS
|
||||
PCLIENT
|
||||
PCLONG
|
||||
PCOBJECT
|
||||
pcon
|
||||
PCONSOLE
|
||||
PCONSOLEENDTASK
|
||||
@@ -1387,6 +1370,7 @@ pcshell
|
||||
PCSHORT
|
||||
PCSR
|
||||
PCSTR
|
||||
PCUNICODE
|
||||
PCWCH
|
||||
PCWCHAR
|
||||
PCWSTR
|
||||
@@ -1435,6 +1419,7 @@ PLOGICAL
|
||||
pnm
|
||||
PNMLINK
|
||||
pntm
|
||||
PNTSTATUS
|
||||
POBJECT
|
||||
Podcast
|
||||
POINTSLIST
|
||||
@@ -1452,7 +1437,9 @@ PPEB
|
||||
ppf
|
||||
ppguid
|
||||
ppidl
|
||||
pplx
|
||||
PPROC
|
||||
PPROCESS
|
||||
ppropvar
|
||||
ppsi
|
||||
ppsl
|
||||
@@ -1516,6 +1503,7 @@ ptrs
|
||||
ptsz
|
||||
PTYIn
|
||||
PUCHAR
|
||||
PUNICODE
|
||||
pwch
|
||||
PWDDMCONSOLECONTEXT
|
||||
pws
|
||||
@@ -1577,6 +1565,7 @@ REGISTEROS
|
||||
REGISTERVDM
|
||||
regkey
|
||||
REGSTR
|
||||
reingest
|
||||
RELBINPATH
|
||||
remoting
|
||||
renamer
|
||||
@@ -1588,7 +1577,6 @@ replatformed
|
||||
Replymessage
|
||||
repositorypath
|
||||
Requiresx
|
||||
rerasterize
|
||||
rescap
|
||||
Resequence
|
||||
RESETCONTENT
|
||||
@@ -1777,6 +1765,7 @@ somefile
|
||||
SOURCEBRANCH
|
||||
sourced
|
||||
spammy
|
||||
spand
|
||||
SRCCODEPAGE
|
||||
SRCCOPY
|
||||
SRCINVERT
|
||||
@@ -1784,13 +1773,10 @@ srcsrv
|
||||
SRCSRVTRG
|
||||
srctool
|
||||
srect
|
||||
srgb
|
||||
Srgb
|
||||
srv
|
||||
srvinit
|
||||
srvpipe
|
||||
ssa
|
||||
startdir
|
||||
STARTF
|
||||
STARTUPINFO
|
||||
STARTUPINFOEX
|
||||
@@ -1800,7 +1786,6 @@ STARTWPARMS
|
||||
STARTWPARMSA
|
||||
STARTWPARMSW
|
||||
Statusline
|
||||
stb
|
||||
stdafx
|
||||
STDAPI
|
||||
stdc
|
||||
@@ -1874,7 +1859,6 @@ TDP
|
||||
TEAMPROJECT
|
||||
tearoff
|
||||
Teb
|
||||
Techo
|
||||
tellp
|
||||
teraflop
|
||||
terminalcore
|
||||
@@ -1966,7 +1950,6 @@ trx
|
||||
tsattrs
|
||||
tsf
|
||||
tsgr
|
||||
tsm
|
||||
TStr
|
||||
TSTRFORMAT
|
||||
TSub
|
||||
@@ -2018,6 +2001,7 @@ unittesting
|
||||
unittests
|
||||
unk
|
||||
unknwn
|
||||
unmark
|
||||
UNORM
|
||||
unparseable
|
||||
unregistering
|
||||
@@ -2052,7 +2036,6 @@ USRDLL
|
||||
utr
|
||||
UVWX
|
||||
UVWXY
|
||||
UVWXYZ
|
||||
uwa
|
||||
uwp
|
||||
uxtheme
|
||||
@@ -2063,7 +2046,6 @@ vcpkg
|
||||
vcprintf
|
||||
vcxitems
|
||||
vec
|
||||
vectorize
|
||||
vectorized
|
||||
VERCTRL
|
||||
VERTBAR
|
||||
@@ -2132,6 +2114,7 @@ WDDMCONSOLECONTEXT
|
||||
wdm
|
||||
webpage
|
||||
websites
|
||||
websockets
|
||||
wekyb
|
||||
wex
|
||||
wextest
|
||||
@@ -2303,6 +2286,7 @@ xunit
|
||||
xutr
|
||||
XVIRTUALSCREEN
|
||||
XWalk
|
||||
xwwyzz
|
||||
xxyyzz
|
||||
yact
|
||||
YCast
|
||||
@@ -2313,9 +2297,7 @@ YOffset
|
||||
YSubstantial
|
||||
YVIRTUALSCREEN
|
||||
YWalk
|
||||
Zab
|
||||
zabcd
|
||||
Zabcdefghijklmn
|
||||
Zabcdefghijklmnopqrstuvwxyz
|
||||
ZCmd
|
||||
ZCtrl
|
||||
|
||||
2
.github/actions/spelling/patterns/0_t.txt
vendored
2
.github/actions/spelling/patterns/0_t.txt
vendored
@@ -10,4 +10,4 @@
|
||||
\\tests(?![a-z])
|
||||
\\thread(?![a-z])
|
||||
\\tools(?![a-z])
|
||||
\\types?(?![a-z])
|
||||
\\types(?![a-z])
|
||||
|
||||
@@ -27,12 +27,6 @@ ROY\sG\.\sBIV
|
||||
# Python stringprefix / binaryprefix
|
||||
\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'
|
||||
|
||||
# SSE intrinsics like "_mm_subs_epu16"
|
||||
\b_mm(?:|256|512)_\w+\b
|
||||
|
||||
# ARM NEON intrinsics like "vsubq_u16"
|
||||
\bv\w+_[fsu](?:8|16|32|64)\b
|
||||
|
||||
# Automatically suggested patterns
|
||||
# hit-count: 3831 file-count: 582
|
||||
# IServiceProvider
|
||||
@@ -41,7 +35,7 @@ ROY\sG\.\sBIV
|
||||
# hit-count: 71 file-count: 35
|
||||
# Compiler flags
|
||||
(?:^|[\t ,"'`=(])-[D](?=[A-Z]{2,}|[A-Z][a-z])
|
||||
(?:^|[\t ,"'`=(])-[X](?!aml)(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
(?:^|[\t ,"'`=(])-[X](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# hit-count: 41 file-count: 28
|
||||
# version suffix <word>v#
|
||||
|
||||
3156
.github/fabricbot.json
vendored
Normal file
3156
.github/fabricbot.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
751
.github/policies/resourceManagement.yml
vendored
751
.github/policies/resourceManagement.yml
vendored
@@ -1,751 +0,0 @@
|
||||
id:
|
||||
name: GitOps.PullRequestIssueManagement
|
||||
description: GitOps.PullRequestIssueManagement primitive
|
||||
owner:
|
||||
resource: repository
|
||||
disabled: false
|
||||
where:
|
||||
configuration:
|
||||
resourceManagementConfiguration:
|
||||
scheduledSearches:
|
||||
- description:
|
||||
frequencies:
|
||||
- hourly:
|
||||
hour: 3
|
||||
filters:
|
||||
- isIssue
|
||||
- isOpen
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- hasLabel:
|
||||
label: No-Recent-Activity
|
||||
- noActivitySince:
|
||||
days: 3
|
||||
actions:
|
||||
- closeIssue
|
||||
- description:
|
||||
frequencies:
|
||||
- hourly:
|
||||
hour: 3
|
||||
filters:
|
||||
- isIssue
|
||||
- isOpen
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- noActivitySince:
|
||||
days: 4
|
||||
- isNotLabeledWith:
|
||||
label: No-Recent-Activity
|
||||
actions:
|
||||
- addLabel:
|
||||
label: No-Recent-Activity
|
||||
- addReply:
|
||||
reply: This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **4 days**. It will be closed if no further activity occurs **within 3 days of this comment**.
|
||||
- description:
|
||||
frequencies:
|
||||
- hourly:
|
||||
hour: 3
|
||||
filters:
|
||||
- isIssue
|
||||
- isOpen
|
||||
- hasLabel:
|
||||
label: Resolution-Duplicate
|
||||
- noActivitySince:
|
||||
days: 1
|
||||
actions:
|
||||
- addReply:
|
||||
reply: This issue has been marked as duplicate and has not had any activity for **1 day**. It will be closed for housekeeping purposes.
|
||||
- closeIssue
|
||||
- description:
|
||||
frequencies:
|
||||
- hourly:
|
||||
hour: 3
|
||||
filters:
|
||||
- isPullRequest
|
||||
- isOpen
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- hasLabel:
|
||||
label: No-Recent-Activity
|
||||
- noActivitySince:
|
||||
days: 7
|
||||
actions:
|
||||
- closeIssue
|
||||
- description:
|
||||
frequencies:
|
||||
- hourly:
|
||||
hour: 3
|
||||
filters:
|
||||
- isPullRequest
|
||||
- isOpen
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- noActivitySince:
|
||||
days: 7
|
||||
- isNotLabeledWith:
|
||||
label: No-Recent-Activity
|
||||
actions:
|
||||
- addLabel:
|
||||
label: No-Recent-Activity
|
||||
- addReply:
|
||||
reply: This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **7 days**. It will be closed if no further activity occurs **within 7 days of this comment**.
|
||||
eventResponderTasks:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- or:
|
||||
- and:
|
||||
- isAction:
|
||||
action: Opened
|
||||
- not:
|
||||
hasLabel:
|
||||
label: ⛺ Reserved
|
||||
then:
|
||||
- addLabel:
|
||||
label: Needs-Triage
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- isAction:
|
||||
action: Created
|
||||
- isActivitySender:
|
||||
issueAuthor: True
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
then:
|
||||
- addLabel:
|
||||
label: Needs-Attention
|
||||
- removeLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- not:
|
||||
isAction:
|
||||
action: Closed
|
||||
- hasLabel:
|
||||
label: No-Recent-Activity
|
||||
then:
|
||||
- removeLabel:
|
||||
label: No-Recent-Activity
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- hasLabel:
|
||||
label: No-Recent-Activity
|
||||
then:
|
||||
- removeLabel:
|
||||
label: No-Recent-Activity
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request_Review
|
||||
- isAction:
|
||||
action: Submitted
|
||||
- isReviewState:
|
||||
reviewState: Changes_requested
|
||||
then:
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request
|
||||
- isActivitySender:
|
||||
issueAuthor: True
|
||||
- not:
|
||||
isAction:
|
||||
action: Closed
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- isActivitySender:
|
||||
issueAuthor: True
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request_Review
|
||||
- isActivitySender:
|
||||
issueAuthor: True
|
||||
- hasLabel:
|
||||
label: Needs-Author-Feedback
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request
|
||||
- not:
|
||||
isAction:
|
||||
action: Closed
|
||||
- hasLabel:
|
||||
label: No-Recent-Activity
|
||||
then:
|
||||
- removeLabel:
|
||||
label: No-Recent-Activity
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- hasLabel:
|
||||
label: No-Recent-Activity
|
||||
then:
|
||||
- removeLabel:
|
||||
label: No-Recent-Activity
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request_Review
|
||||
- hasLabel:
|
||||
label: No-Recent-Activity
|
||||
then:
|
||||
- removeLabel:
|
||||
label: No-Recent-Activity
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request
|
||||
- hasLabel:
|
||||
label: AutoMerge
|
||||
then:
|
||||
- enableAutoMerge:
|
||||
mergeMethod: Squash
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request
|
||||
- labelRemoved:
|
||||
label: AutoMerge
|
||||
then:
|
||||
- disableAutoMerge
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- or:
|
||||
- and:
|
||||
- isLabeled
|
||||
- hasLabel:
|
||||
label: Mass-Chaos
|
||||
- isOpen
|
||||
- isLabeled
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Needs-Tag-Fix
|
||||
- or:
|
||||
- and:
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Accessibility
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Build
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Extensibility
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Fonts
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Input
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Interaction
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Interop
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Output
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Performance
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Rendering
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Server
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Settings
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-TerminalConnection
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-TerminalControl
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-User Interface
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-VT
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-CodeHealth
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Quality
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-AzureShell
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Schema
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Commandline
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-ShellExtension
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-WPFControl
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Settings UI
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-DefApp
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Remoting
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Windowing
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Theming
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Area-Localization
|
||||
- and:
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Issue-Bug
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Issue-Docs
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Issue-Feature
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Issue-Question
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Issue-Samples
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Issue-Task
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Issue-Scenario
|
||||
- and:
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-Cmd.exe
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-Colortool
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-Conhost
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-Conpty
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-Meta
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-Powershell
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-Terminal
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Product-WSL
|
||||
- and:
|
||||
- not: isOpen
|
||||
- and:
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-Answered
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-By-Design
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-Duplicate
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-External
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-Fix-Available
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-Fix-Committed
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-Won't-Fix
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Needs-Triage
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Resolution-Duplicate
|
||||
- not:
|
||||
hasLabel:
|
||||
label: ⛺ Reserved
|
||||
- not:
|
||||
hasLabel:
|
||||
label: Tracking-External
|
||||
then:
|
||||
- addLabel:
|
||||
label: Needs-Tag-Fix
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- and:
|
||||
- isLabeled
|
||||
- hasLabel:
|
||||
label: Needs-Tag-Fix
|
||||
- and:
|
||||
- or:
|
||||
- hasLabel:
|
||||
label: Area-Accessibility
|
||||
- hasLabel:
|
||||
label: Area-Build
|
||||
- hasLabel:
|
||||
label: Area-Extensibility
|
||||
- hasLabel:
|
||||
label: Area-Fonts
|
||||
- hasLabel:
|
||||
label: Area-Input
|
||||
- hasLabel:
|
||||
label: Area-Interaction
|
||||
- hasLabel:
|
||||
label: Area-Interop
|
||||
- hasLabel:
|
||||
label: Area-Output
|
||||
- hasLabel:
|
||||
label: Area-Performance
|
||||
- hasLabel:
|
||||
label: Area-Rendering
|
||||
- hasLabel:
|
||||
label: Area-Server
|
||||
- hasLabel:
|
||||
label: Area-Settings
|
||||
- hasLabel:
|
||||
label: Area-TerminalConnection
|
||||
- hasLabel:
|
||||
label: Area-TerminalControl
|
||||
- hasLabel:
|
||||
label: Area-User Interface
|
||||
- hasLabel:
|
||||
label: Area-VT
|
||||
- hasLabel:
|
||||
label: Area-CodeHealth
|
||||
- hasLabel:
|
||||
label: Area-Quality
|
||||
- hasLabel:
|
||||
label: Area-Schema
|
||||
- hasLabel:
|
||||
label: Area-AzureShell
|
||||
- hasLabel:
|
||||
label: Area-Commandline
|
||||
- hasLabel:
|
||||
label: Area-ShellExtension
|
||||
- hasLabel:
|
||||
label: Area-WPFControl
|
||||
- hasLabel:
|
||||
label: Area-Settings UI
|
||||
- hasLabel:
|
||||
label: Area-DefApp
|
||||
- hasLabel:
|
||||
label: Area-Localization
|
||||
- hasLabel:
|
||||
label: Area-Windowing
|
||||
- hasLabel:
|
||||
label: Area-Theming
|
||||
- hasLabel:
|
||||
label: Area-AtlasEngine
|
||||
- hasLabel:
|
||||
label: Area-CmdPal
|
||||
- or:
|
||||
- hasLabel:
|
||||
label: Issue-Bug
|
||||
- hasLabel:
|
||||
label: Issue-Docs
|
||||
- hasLabel:
|
||||
label: Issue-Feature
|
||||
- hasLabel:
|
||||
label: Issue-Question
|
||||
- hasLabel:
|
||||
label: Issue-Samples
|
||||
- hasLabel:
|
||||
label: Issue-Task
|
||||
- hasLabel:
|
||||
label: Issue-Scenario
|
||||
- or:
|
||||
- hasLabel:
|
||||
label: Product-Cmd.exe
|
||||
- hasLabel:
|
||||
label: Product-Colortool
|
||||
- hasLabel:
|
||||
label: Product-Conhost
|
||||
- hasLabel:
|
||||
label: Product-Conpty
|
||||
- hasLabel:
|
||||
label: Product-Meta
|
||||
- hasLabel:
|
||||
label: Product-Powershell
|
||||
- hasLabel:
|
||||
label: Product-Terminal
|
||||
- hasLabel:
|
||||
label: Product-WSL
|
||||
- or:
|
||||
- isOpen
|
||||
- and:
|
||||
- not: isOpen
|
||||
- or:
|
||||
- hasLabel:
|
||||
label: Resolution-Answered
|
||||
- hasLabel:
|
||||
label: Resolution-By-Design
|
||||
- hasLabel:
|
||||
label: Resolution-Duplicate
|
||||
- hasLabel:
|
||||
label: Resolution-External
|
||||
- hasLabel:
|
||||
label: Resolution-Fix-Available
|
||||
- hasLabel:
|
||||
label: Resolution-Fix-Committed
|
||||
- hasLabel:
|
||||
label: Resolution-Won't-Fix
|
||||
- hasLabel:
|
||||
label: Tracking-External
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Tag-Fix
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request
|
||||
then:
|
||||
- inPrLabel:
|
||||
label: In-PR
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- hasLabel:
|
||||
label: Needs-Tag-Fix
|
||||
- hasLabel:
|
||||
label: Resolution-Duplicate
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Tag-Fix
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- or:
|
||||
- titleContains:
|
||||
pattern: ^\s*Bug Report \(IF I DO NOT CHANGE THIS THE ISSUE WILL BE AUTO-CLOSED\)\s*$
|
||||
isRegex: True
|
||||
- titleContains:
|
||||
pattern: ^\s*Bug Report\s*$
|
||||
isRegex: True
|
||||
- or:
|
||||
- isAction:
|
||||
action: Opened
|
||||
- isAction:
|
||||
action: Reopened
|
||||
- not:
|
||||
activitySenderHasPermission:
|
||||
permission: Write
|
||||
- not:
|
||||
activitySenderHasPermission:
|
||||
permission: Admin
|
||||
then:
|
||||
- closeIssue
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- addReply:
|
||||
reply: Hi! Thanks for attempting to open an issue. Unfortunately, your title wasn't changed from the original template which makes it very hard for us to track and triage. You are welcome to fix up the title and try again with a new issue.
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- or:
|
||||
- isAction:
|
||||
action: Opened
|
||||
- isAction:
|
||||
action: Reopened
|
||||
- or:
|
||||
- not:
|
||||
bodyContains:
|
||||
pattern: .+
|
||||
isRegex: True
|
||||
then:
|
||||
- closeIssue
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- addReply:
|
||||
reply: "Hi! Thanks for attempting to open an issue. Unfortunately, you didn't write anything in the body which makes it impossible to understand your concern. You are welcome to fix up the issue and try again by opening another issue with the body filled out. "
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request
|
||||
- isLabeled
|
||||
- hasLabel:
|
||||
label: Needs-Second
|
||||
- isOpen
|
||||
then:
|
||||
- requestReview:
|
||||
reviewer: zadjii-msft
|
||||
- requestReview:
|
||||
reviewer: PankajBhojwani
|
||||
- requestReview:
|
||||
reviewer: carlos-zamora
|
||||
- requestReview:
|
||||
reviewer: dhowett
|
||||
- requestReview:
|
||||
reviewer: lhecker
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request_Review
|
||||
- not: isOpen
|
||||
- hasLabel:
|
||||
label: Needs-Second
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Second
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issues
|
||||
- hasLabel:
|
||||
label: In-PR
|
||||
- hasLabel:
|
||||
label: Help Wanted
|
||||
- isLabeled
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Help-Wanted
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: '\/dup(licate|e)?(\s+of)?\s+\#[\d]+'
|
||||
isRegex: True
|
||||
- or:
|
||||
- activitySenderHasPermission:
|
||||
permission: Admin
|
||||
- activitySenderHasPermission:
|
||||
permission: Write
|
||||
then:
|
||||
- addReply:
|
||||
reply: Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!
|
||||
- closeIssue
|
||||
- removeLabel:
|
||||
label: Needs-Triage
|
||||
- addLabel:
|
||||
label: Resolution-Duplicate
|
||||
- removeLabel:
|
||||
label: Needs-Tag-Fix
|
||||
- removeLabel:
|
||||
label: Needs-Attention
|
||||
- removeLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- removeLabel:
|
||||
label: Needs-Repro
|
||||
- removeLabel:
|
||||
label: Needs-Second
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: '\/feedback'
|
||||
isRegex: True
|
||||
- or:
|
||||
- activitySenderHasPermission:
|
||||
permission: Admin
|
||||
- activitySenderHasPermission:
|
||||
permission: Write
|
||||
then:
|
||||
- addReply:
|
||||
reply: >2-
|
||||
|
||||
Hi there!<br><br>Can you please send us feedback with the [Feedback Hub](https://support.microsoft.com/en-us/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332) with this issue? Make sure to click the "Start recording" button, then reproduce the issue before submitting the feedback. Once it's submitted, paste the link here so we can more easily find your crash information on the back end?<br><br>Thanks!<br><br><br><br><br><br>
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
then:
|
||||
- cleanEmailReply
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Pull_Request
|
||||
then:
|
||||
- labelSync:
|
||||
pattern: Issue-
|
||||
- labelSync:
|
||||
pattern: Area-
|
||||
- labelSync:
|
||||
pattern: Priority-
|
||||
- labelSync:
|
||||
pattern: Product-
|
||||
- labelSync:
|
||||
pattern: Severity-
|
||||
- labelSync:
|
||||
pattern: Impact-
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: '\/dup(licate|e)?(\s+of)?\s+https'
|
||||
isRegex: True
|
||||
- or:
|
||||
- activitySenderHasPermission:
|
||||
permission: Admin
|
||||
- activitySenderHasPermission:
|
||||
permission: Write
|
||||
then:
|
||||
- addReply:
|
||||
reply: Hi! We've identified this issue as a duplicate of one that exists on somebody else's Issue Tracker. Please make sure you subscribe to the referenced external issue for future updates. Thanks for your report!
|
||||
- closeIssue
|
||||
- removeLabel:
|
||||
label: Needs-Triage
|
||||
- addLabel:
|
||||
label: Resolution-External
|
||||
- removeLabel:
|
||||
label: Needs-Attention
|
||||
- removeLabel:
|
||||
label: Needs-Author-Feedback
|
||||
- removeLabel:
|
||||
label: Needs-Bisect
|
||||
- removeLabel:
|
||||
label: Needs-Repro
|
||||
- removeLabel:
|
||||
label: Needs-Second
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: /?
|
||||
isRegex: False
|
||||
- or:
|
||||
- activitySenderHasPermission:
|
||||
permission: Admin
|
||||
- activitySenderHasPermission:
|
||||
permission: Write
|
||||
then:
|
||||
- removeLabel:
|
||||
label: Needs-Attention
|
||||
- addLabel:
|
||||
label: Needs-Author-Feedback
|
||||
description:
|
||||
onFailure:
|
||||
onSuccess:
|
||||
24
.github/workflows/winget.yml
vendored
24
.github/workflows/winget.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Publish to Winget
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
env:
|
||||
REGEX: 'Microsoft\.WindowsTerminal(?:Preview)?_([\d.]+)_8wekyb3d8bbwe\.msixbundle$'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: windows-latest # Action can only run on Windows
|
||||
steps:
|
||||
- name: Publish Windows Terminal ${{ github.event.release.prerelease && 'Preview' || 'Stable' }}
|
||||
run: |
|
||||
$assets = '${{ toJSON(github.event.release.assets) }}' | ConvertFrom-Json
|
||||
$wingetRelevantAsset = $assets | Where-Object { $_.name -like '*.msixbundle' } | Select-Object -First 1
|
||||
$regex = [Regex]::New($env:REGEX)
|
||||
$version = $regex.Match($wingetRelevantAsset.name).Groups[1].Value
|
||||
|
||||
$wingetPackage = "Microsoft.WindowsTerminal${{ github.event.release.prerelease && '.Preview' || '' }}"
|
||||
|
||||
& curl.exe -JLO https://aka.ms/wingetcreate/latest
|
||||
& .\wingetcreate.exe update $wingetPackage -s -v $version -u $wingetRelevantAsset.browser_download_url -t "${{ secrets.WINGET_TOKEN }}"
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -6,7 +6,7 @@
|
||||
"C_Cpp.loggingLevel": "None",
|
||||
"files.associations": {
|
||||
"xstring": "cpp",
|
||||
"*.idl": "midl3",
|
||||
"*.idl": "cpp",
|
||||
"array": "cpp",
|
||||
"future": "cpp",
|
||||
"istream": "cpp",
|
||||
@@ -103,9 +103,7 @@
|
||||
"files.exclude": {
|
||||
"**/bin/**": true,
|
||||
"**/obj/**": true,
|
||||
"**/packages/**": true,
|
||||
"**/Generated Files/**": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/packages/**": true
|
||||
}
|
||||
}
|
||||
}
|
||||
68
NOTICE.md
68
NOTICE.md
@@ -276,53 +276,41 @@ OTHER DEALINGS IN THE SOFTWARE.
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
```
|
||||
|
||||
## stb
|
||||
|
||||
**Source**: [https://github.com/nothings/stb](https://github.com/nothings/stb)
|
||||
## ConEmu
|
||||
**Source**: [https://github.com/Maximus5/ConEmu](https://github.com/Maximus5/ConEmu)
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
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 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.
|
||||
```
|
||||
BSD 3-Clause License
|
||||
|
||||
## Oklab
|
||||
**Source**: [https://bottosson.github.io/posts/oklab/](https://bottosson.github.io/posts/oklab/)
|
||||
Copyright (c) 2009-2017, Maximus5 <ConEmu.Maximus5@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
### License
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
```
|
||||
Copyright (c) 2020 Björn Ottosson
|
||||
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.
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
```
|
||||
|
||||
# Microsoft Open Source
|
||||
|
||||
@@ -326,9 +326,6 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.Tests.Feature", "src\winconpty\ft_pty\winconpty.FeatureTests.vcxproj", "{024052DE-83FB-4653-AEA4-90790D29D5BD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAzBridge", "src\cascadia\TerminalAzBridge\TerminalAzBridge.vcxproj", "{067F0A06-FCB7-472C-96E9-B03B54E8E18D}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "src\dep\fmt\fmt.vcxproj", "{6BAE5851-50D5-4934-8D5E-30361A8A40F3}"
|
||||
EndProject
|
||||
@@ -418,10 +415,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MidiAudio", "src\audio\midi
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TerminalStress", "src\tools\TerminalStress\TerminalStress.csproj", "{613CCB57-5FA9-48EF-80D0-6B1E319E20C4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderingTests", "src\tools\RenderingTests\RenderingTests.vcxproj", "{37C995E0-2349-4154-8E77-4A52C0C7F46D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "benchcat", "src\tools\benchcat\benchcat.vcxproj", "{2C836962-9543-4CE5-B834-D28E1F124B66}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|Any CPU = AuditMode|Any CPU
|
||||
@@ -2774,46 +2767,6 @@ Global
|
||||
{613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{613CCB57-5FA9-48EF-80D0-6B1E319E20C4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.AuditMode|ARM64.ActiveCfg = Release|ARM64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x64.ActiveCfg = Release|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|ARM64.ActiveCfg = Release|ARM64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Release|x64.ActiveCfg = Release|x64
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66}.Release|x86.ActiveCfg = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2919,8 +2872,6 @@ Global
|
||||
{40BD8415-DD93-4200-8D82-498DDDC08CC8} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{3C67784E-1453-49C2-9660-483E2CC7F7AD} = {40BD8415-DD93-4200-8D82-498DDDC08CC8}
|
||||
{613CCB57-5FA9-48EF-80D0-6B1E319E20C4} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
{2C836962-9543-4CE5-B834-D28E1F124B66} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
||||
@@ -55,8 +55,14 @@ Copy-Item "build\helix\runtests.cmd" $payloadDir
|
||||
Copy-Item "build\helix\InstallTestAppDependencies.ps1" "$payloadDir"
|
||||
Copy-Item "build\Helix\EnsureMachineState.ps1" "$payloadDir"
|
||||
|
||||
# Extract the unpackaged distribution of Windows Terminal to the payload directory,
|
||||
# where it will create a subdirectory named terminal-0.0.1.0
|
||||
# This is referenced in TerminalApp.cs later as part of the test harness.
|
||||
& tar -x -v -f "$repoDirectory\Artifacts\$ArtifactName\unpackaged\WindowsTerminalDev_0.0.1.0_x64.zip" -C "$payloadDir"
|
||||
Copy-Item "res\fonts\*.ttf" "$payloadDir\terminal-0.0.1.0"
|
||||
# Copy the APPX package from the 'drop' artifact dir and Windows Kits
|
||||
Copy-Item "$repoDirectory\Artifacts\$ArtifactName\appx\CascadiaPackage_0.0.1.0_$Platform.msix" $payloadDir\CascadiaPackage.zip
|
||||
Copy-Item "C:\program files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs\Microsoft.VCLibs.Desktop\14.0\Appx\Retail\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" $payloadDir\VCLibs.zip
|
||||
|
||||
# Rename it to extension of ZIP because Expand-Archive is real sassy on the build machines
|
||||
# and refuses to unzip it because of its file extension while on a desktop, it just
|
||||
# does the job without complaining.
|
||||
|
||||
# Extract the APPX package
|
||||
Expand-Archive -LiteralPath $payloadDir\CascadiaPackage.zip -DestinationPath $payloadDir\appx
|
||||
Expand-Archive -LiteralPath $payloadDir\VCLibs.zip -DestinationPath $payloadDir\appx -Force
|
||||
|
||||
@@ -70,7 +70,7 @@ foreach ($testRun in $testRuns.value)
|
||||
|
||||
foreach ($testResult in $testResults.value)
|
||||
{
|
||||
$info = ConvertFrom-Json ([System.Web.HttpUtility]::HtmlDecode($testResult.comment))
|
||||
$info = ConvertFrom-Json $testResult.comment
|
||||
$helixJobId = $info.HelixJobId
|
||||
$helixWorkItemName = $info.HelixWorkItemName
|
||||
|
||||
|
||||
@@ -67,6 +67,51 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
// THIRD PARTY SOFTWARE
|
||||
"MatchedPath": [
|
||||
"cpprest*.dll"
|
||||
],
|
||||
"SigningInfo": {
|
||||
"Operations": [
|
||||
{
|
||||
"KeyCode": "CP-231522",
|
||||
"OperationSetCode": "SigntoolSign",
|
||||
"Parameters": [
|
||||
{
|
||||
"parameterName": "OpusName",
|
||||
"parameterValue": "Microsoft"
|
||||
},
|
||||
{
|
||||
"parameterName": "OpusInfo",
|
||||
"parameterValue": "http://www.microsoft.com"
|
||||
},
|
||||
{
|
||||
"parameterName": "FileDigest",
|
||||
"parameterValue": "/fd \"SHA256\""
|
||||
},
|
||||
{
|
||||
"parameterName": "PageHash",
|
||||
"parameterValue": "/NPH"
|
||||
},
|
||||
{
|
||||
"parameterName": "TimeStamp",
|
||||
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
|
||||
}
|
||||
],
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"KeyCode": "CP-231522",
|
||||
"OperationSetCode": "SigntoolVerify",
|
||||
"Parameters": [],
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<!-- This cannot be included in another project that depends on XAML (as it would be a duplicate package ID) -->
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
<package id="Microsoft.Debugging.Tools.PdbStr" version="20220617.1556.0" targetFramework="native" />
|
||||
<package id="Microsoft.Debugging.Tools.SrcTool" version="20220617.1556.0" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -56,10 +56,15 @@ parameters:
|
||||
- x64
|
||||
- x86
|
||||
- arm64
|
||||
- name: buildWindowsVersions
|
||||
type: object
|
||||
default:
|
||||
- Win10
|
||||
- Win11
|
||||
|
||||
variables:
|
||||
MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe'
|
||||
TerminalInternalPackageVersion: "0.0.8"
|
||||
TerminalInternalPackageVersion: "0.0.7"
|
||||
# If we are building a branch called "release-*", change the NuGet suffix
|
||||
# to "preview". If we don't do that, XES will set the suffix to "release1"
|
||||
# because it truncates the value after the first period.
|
||||
@@ -82,6 +87,13 @@ variables:
|
||||
NuGetPackBetaVersion: preview
|
||||
${{ elseif eq(variables['Build.SourceBranchName'], 'main') }}:
|
||||
NuGetPackBetaVersion: experimental
|
||||
# The NuGet packages have to use *somebody's* DLLs. We used to force them to
|
||||
# use the Win10 build outputs, but if there isn't a Win10 build we should use
|
||||
# the Win11 one.
|
||||
${{ if containsValue(parameters.buildWindowsVersions, 'Win10') }}:
|
||||
TerminalBestVersionForNuGetPackages: Win10
|
||||
${{ else }}:
|
||||
TerminalBestVersionForNuGetPackages: Win11
|
||||
|
||||
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
|
||||
resources:
|
||||
@@ -95,9 +107,11 @@ jobs:
|
||||
matrix:
|
||||
${{ each config in parameters.buildConfigurations }}:
|
||||
${{ each platform in parameters.buildPlatforms }}:
|
||||
${{ config }}_${{ platform }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
BuildPlatform: ${{ platform }}
|
||||
${{ each windowsVersion in parameters.buildWindowsVersions }}:
|
||||
${{ config }}_${{ platform }}_${{ windowsVersion }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
BuildPlatform: ${{ platform }}
|
||||
TerminalTargetWindowsVersion: ${{ windowsVersion }}
|
||||
displayName: Build
|
||||
timeoutInMinutes: 240
|
||||
cancelTimeoutInMinutes: 1
|
||||
@@ -171,6 +185,10 @@ jobs:
|
||||
arguments: -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
|
||||
pwsh: true
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- pwsh: |-
|
||||
./build/scripts/Patch-ManifestsToWindowsVersion.ps1 -NewWindowsVersion "10.0.22000.0"
|
||||
displayName: Update manifest target version to Win11 (if necessary)
|
||||
condition: and(succeeded(), eq(variables['TerminalTargetWindowsVersion'], 'Win11'))
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln
|
||||
condition: true
|
||||
@@ -187,7 +205,7 @@ jobs:
|
||||
continueOnError: True
|
||||
inputs:
|
||||
PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog
|
||||
ArtifactName: binlog-$(BuildPlatform)
|
||||
ArtifactName: binlog-$(BuildPlatform)-$(TerminalTargetWindowsVersion)
|
||||
- task: PowerShell@2
|
||||
displayName: Check MSIX for common regressions
|
||||
inputs:
|
||||
@@ -236,15 +254,23 @@ jobs:
|
||||
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy *.msix and symbols to Artifacts
|
||||
displayName: Copy *.appx/*.msix to Artifacts
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/*.appx
|
||||
|
||||
**/*.msix
|
||||
|
||||
**/*.appxsym
|
||||
|
||||
!**/Microsoft.VCLibs*.appx
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/appx
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
|
||||
displayName: 'Generate SBOM manifest'
|
||||
inputs:
|
||||
BuildDropPath: '$(System.ArtifactsDirectory)/appx'
|
||||
|
||||
- pwsh: |-
|
||||
$Package = (Get-ChildItem "$(Build.ArtifactStagingDirectory)/appx" -Recurse -Filter "Cascadia*.msix" | Select -First 1)
|
||||
@@ -267,30 +293,11 @@ jobs:
|
||||
& "$(MakeAppxPath)" pack /h SHA256 /o /p $PackageFilename /d "$(Build.SourcesDirectory)\UnpackedTerminalPackage"
|
||||
displayName: Re-pack the new Terminal package after signing
|
||||
|
||||
- pwsh: |-
|
||||
$XamlAppxPath = (Get-Item "src\cascadia\CascadiaPackage\AppPackages\*\Dependencies\$(BuildPlatform)\Microsoft.UI.Xaml*.appx").FullName
|
||||
& .\build\scripts\New-UnpackagedTerminalDistribution.ps1 -TerminalAppX $(WindowsTerminalPackagePath) -XamlAppX $XamlAppxPath -Destination "$(Build.ArtifactStagingDirectory)/appx"
|
||||
displayName: Build Unpackaged Distribution
|
||||
|
||||
- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
|
||||
displayName: 'Generate SBOM manifest (application)'
|
||||
inputs:
|
||||
BuildDropPath: '$(System.ArtifactsDirectory)/appx'
|
||||
|
||||
- task: DropValidatorTask@0
|
||||
displayName: 'Validate application SBOM manifest'
|
||||
inputs:
|
||||
BuildDropPath: '$(System.ArtifactsDirectory)/appx'
|
||||
OutputPath: 'output.json'
|
||||
ValidateSignature: true
|
||||
Verbosity: 'Verbose'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (Terminal app)
|
||||
displayName: Publish Artifact (appx)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/appx
|
||||
ArtifactName: terminal-$(BuildPlatform)-$(BuildConfiguration)
|
||||
|
||||
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion)
|
||||
- ${{ if eq(parameters.buildConPTY, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy ConPTY to Artifacts
|
||||
@@ -308,7 +315,7 @@ jobs:
|
||||
displayName: Publish Artifact (ConPTY)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/conpty
|
||||
ArtifactName: conpty-dll-$(BuildPlatform)-$(BuildConfiguration)
|
||||
ArtifactName: conpty-dll-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion)
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy PublicTerminalCore.dll to Artifacts
|
||||
@@ -322,7 +329,7 @@ jobs:
|
||||
displayName: Publish Artifact (PublicTerminalCore)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf
|
||||
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)
|
||||
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion)
|
||||
|
||||
- task: PublishSymbols@2
|
||||
displayName: Publish symbols path
|
||||
@@ -340,6 +347,11 @@ jobs:
|
||||
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- job: BundleAndSign
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each windowsVersion in parameters.buildWindowsVersions }}:
|
||||
${{ windowsVersion }}:
|
||||
TerminalTargetWindowsVersion: ${{ windowsVersion }}
|
||||
displayName: Create and sign AppX/MSIX bundles
|
||||
variables:
|
||||
${{ if eq(parameters.branding, 'Release') }}:
|
||||
@@ -360,13 +372,10 @@ jobs:
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- task: DownloadBuildArtifacts@1
|
||||
displayName: Download Artifacts ${{ platform }}
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Artifacts ${{ platform }} $(TerminalTargetWindowsVersion)
|
||||
inputs:
|
||||
# Make sure to download the entire artifact, because it includes the SPDX SBOM
|
||||
artifactName: terminal-${{ platform }}-Release
|
||||
# Downloading to the source directory should ensure that the later SBOM generator can see the earlier SBOMs.
|
||||
downloadPath: '$(Build.SourcesDirectory)/appx-artifacts'
|
||||
artifactName: appx-${{ platform }}-Release-$(TerminalTargetWindowsVersion)
|
||||
# Add 3000 to the major version component, but only for the bundle.
|
||||
# This is to ensure that it is newer than "2022.xx.yy.zz" or whatever the original bundle versions were before
|
||||
# we switched to uniform naming.
|
||||
@@ -376,7 +385,7 @@ jobs:
|
||||
$Components[0] = ([int]$Components[0] + $VersionEpoch)
|
||||
$BundleVersion = $Components -Join "."
|
||||
New-Item -Type Directory "$(System.ArtifactsDirectory)\bundle"
|
||||
.\build\scripts\Create-AppxBundle.ps1 -InputPath "$(Build.SourcesDirectory)/appx-artifacts" -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\bundle\$(BundleStemName)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
.\build\scripts\Create-AppxBundle.ps1 -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\bundle\$(BundleStemName)_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
displayName: Create WindowsTerminal*.msixbundle
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: Submit *.msixbundle to ESRP for code signing
|
||||
@@ -413,25 +422,11 @@ jobs:
|
||||
}
|
||||
]
|
||||
|
||||
- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
|
||||
displayName: 'Generate SBOM manifest (bundle)'
|
||||
inputs:
|
||||
BuildDropPath: '$(System.ArtifactsDirectory)/bundle'
|
||||
BuildComponentPath: '$(Build.SourcesDirectory)/appx-artifacts'
|
||||
|
||||
- task: DropValidatorTask@0
|
||||
displayName: 'Validate bundle SBOM manifest'
|
||||
inputs:
|
||||
BuildDropPath: '$(System.ArtifactsDirectory)/bundle'
|
||||
OutputPath: 'output.json'
|
||||
ValidateSignature: true
|
||||
Verbosity: 'Verbose'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: appxbundle-signed'
|
||||
inputs:
|
||||
PathtoPublish: $(System.ArtifactsDirectory)\bundle
|
||||
ArtifactName: appxbundle-signed
|
||||
ArtifactName: appxbundle-signed-$(TerminalTargetWindowsVersion)
|
||||
|
||||
- ${{ if eq(parameters.buildConPTY, true) }}:
|
||||
- job: PackageAndSignConPTY
|
||||
@@ -453,10 +448,10 @@ jobs:
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- task: DownloadBuildArtifacts@1
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download ${{ platform }} ConPTY binaries
|
||||
inputs:
|
||||
artifactName: conpty-dll-${{ platform }}-$(BuildConfiguration)
|
||||
artifactName: conpty-dll-${{ platform }}-$(BuildConfiguration)-$(TerminalBestVersionForNuGetPackages)
|
||||
downloadPath: bin\${{ platform }}\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
@@ -544,10 +539,10 @@ jobs:
|
||||
inputs:
|
||||
disableOutputRedirect: true
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- task: DownloadBuildArtifacts@1
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download ${{ platform }} PublicTerminalCore
|
||||
inputs:
|
||||
artifactName: wpf-dll-${{ platform }}-$(BuildConfiguration)
|
||||
artifactName: wpf-dll-${{ platform }}-$(BuildConfiguration)-$(TerminalBestVersionForNuGetPackages)
|
||||
itemPattern: '**/*.dll'
|
||||
downloadPath: bin\${{ platform }}\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
@@ -643,13 +638,13 @@ jobs:
|
||||
|
||||
- template: .\templates\restore-nuget-steps.yml
|
||||
|
||||
# Download the terminal-PLATFORM-CONFIG-VERSION artifact for every platform/version combo
|
||||
# Download the appx-PLATFORM-CONFIG-VERSION artifact for every platform/version combo
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- task: DownloadBuildArtifacts@1
|
||||
displayName: Download Symbols ${{ platform }}
|
||||
inputs:
|
||||
artifactName: terminal-${{ platform }}-Release
|
||||
itemPattern: '**/*.appxsym'
|
||||
- ${{ each windowsVersion in parameters.buildWindowsVersions }}:
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Symbols ${{ platform }} ${{ windowsVersion }}
|
||||
inputs:
|
||||
artifactName: appx-${{ platform }}-Release-${{ windowsVersion }}
|
||||
|
||||
# It seems easier to do this -- download every appxsym -- then enumerate all the PDBs in the build directory for the
|
||||
# public symbol push. Otherwise, we would have to list all of the PDB files one by one.
|
||||
@@ -706,10 +701,10 @@ jobs:
|
||||
submodules: true
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
- task: DownloadBuildArtifacts@1
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Build Artifacts
|
||||
inputs:
|
||||
artifactName: appxbundle-signed
|
||||
artifactName: appxbundle-signed-Win11
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Rename and stage packages for vpack
|
||||
@@ -718,7 +713,7 @@ jobs:
|
||||
script: >-
|
||||
# Rename to known/fixed name for Windows build system
|
||||
|
||||
Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
|
||||
Get-ChildItem Microsoft.WindowsTerminal_Win11_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
|
||||
|
||||
|
||||
# Create vpack directory and place item inside
|
||||
@@ -726,13 +721,13 @@ jobs:
|
||||
mkdir WindowsTerminal.app
|
||||
|
||||
mv Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle .\WindowsTerminal.app\
|
||||
workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed
|
||||
workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed-Win11
|
||||
- task: PkgESVPack@12
|
||||
displayName: 'Package ES - VPack'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
inputs:
|
||||
sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app
|
||||
sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed-Win11\WindowsTerminal.app
|
||||
description: VPack for the Windows Terminal Application
|
||||
pushPkgName: WindowsTerminal.app
|
||||
owner: conhost
|
||||
|
||||
@@ -144,7 +144,7 @@ jobs:
|
||||
inputs:
|
||||
TargetPattern: guardianGlob
|
||||
# See https://aka.ms/gdn-globs for how to do match patterns
|
||||
AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll
|
||||
AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll;-:file|**\cpprest*.dll
|
||||
continueOnError: true
|
||||
|
||||
# Set XES_SERIALPOSTBUILDREADY to run Security and Compliance task once per build
|
||||
|
||||
@@ -64,24 +64,17 @@ steps:
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy *.msix to Artifacts'
|
||||
displayName: 'Copy *.appx/*.msix to Artifacts (Non-PR builds only)'
|
||||
inputs:
|
||||
Contents: |
|
||||
**/*.appx
|
||||
**/*.msix
|
||||
**/*.appxsym
|
||||
!**/Microsoft.VCLibs*.appx
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)/appx'
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
|
||||
- pwsh: |-
|
||||
$TerminalMsixPath = (Get-Item "$(Build.ArtifactStagingDirectory)\appx\Cascadia*.msix").FullName
|
||||
$XamlAppxPath = (Get-Item "src\cascadia\CascadiaPackage\AppPackages\*\Dependencies\$(BuildPlatform)\Microsoft.UI.Xaml*.appx").FullName
|
||||
& .\build\scripts\New-UnpackagedTerminalDistribution.ps1 -TerminalAppX $TerminalMsixPath -XamlAppX $XamlAppxPath -Destination "$(Build.ArtifactStagingDirectory)/unpackaged"
|
||||
displayName: Build Unpackaged Distribution
|
||||
|
||||
- publish: $(Build.ArtifactStagingDirectory)/unpackaged
|
||||
artifact: unpackaged-$(BuildPlatform)-$(BuildConfiguration)
|
||||
displayName: Publish Artifact (unpackaged)
|
||||
condition: succeeded()
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy outputs needed for test runs to Artifacts'
|
||||
|
||||
@@ -14,8 +14,8 @@ parameters:
|
||||
platform: ''
|
||||
# if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline:
|
||||
useBuildOutputFromPipeline: $(System.DefinitionId)
|
||||
openHelixTargetQueues: 'windows.11.amd64.client.open.reunion'
|
||||
closedHelixTargetQueues: 'windows.11.amd64.client.reunion'
|
||||
openHelixTargetQueues: 'windows.10.amd64.client21h1.open.xaml'
|
||||
closedHelixTargetQueues: 'windows.10.amd64.client21h1.xaml'
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
DependsOnTargets="_ConsoleMapWinmdsToManifestFiles">
|
||||
|
||||
<!-- This target is batched and a new Exec is spawned for each entry in _ConsoleWinmdManifest. -->
|
||||
<Exec Command="mt.exe -winmd:"%(_ConsoleWinmdManifest.WinMDPath)" -dll:%(_ConsoleWinmdManifest.Implementation) -out:"%(_ConsoleWinmdManifest.Identity)"" />
|
||||
<Exec Command="mt.exe -winmd:%(_ConsoleWinmdManifest.WinMDPath) -dll:%(_ConsoleWinmdManifest.Implementation) -out:%(_ConsoleWinmdManifest.Identity)" />
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Emit the generated manifest into the Link inputs. -->
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!--
|
||||
This file contains targets that override behavior in Microsoft.UI.Xaml and
|
||||
related packages.
|
||||
|
||||
For example: All XAML needs is a reference to WebView2; it does not need the
|
||||
DLL and it does not need for us to copy the WinMD into the output folder. It
|
||||
also doesn't require the WebView2 loader since we're not actually using
|
||||
WebView2. Therefore, we can get away with *not including the WebView2
|
||||
package* and only adding a reference to its winmd.
|
||||
-->
|
||||
<ItemGroup>
|
||||
<Reference Include="$(WebView2PackageRoot)\lib\Microsoft.Web.WebView2.Core.winmd" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -10,7 +10,7 @@ function Invoke-CheckBadCodeFormatting() {
|
||||
|
||||
# returns a non-zero exit code if there are any diffs in the tracked files in the repo
|
||||
git diff-index --quiet HEAD --
|
||||
if ($LASTEXITCODE -eq 1) {
|
||||
if ($lastExitCode -eq 1) {
|
||||
|
||||
# Write the list of files that need updating to the log
|
||||
git diff-index --name-only HEAD
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
Param(
|
||||
[Parameter(Mandatory,
|
||||
HelpMessage="List of PRI files or XML dumps (detailed only) to merge")]
|
||||
[string[]]
|
||||
$Path,
|
||||
|
||||
[Parameter(Mandatory,
|
||||
HelpMessage="Output Path")]
|
||||
[string]
|
||||
$OutputPath,
|
||||
|
||||
[Parameter(HelpMessage="Name of index in output file; defaults to 'Application'")]
|
||||
[string]
|
||||
$IndexName = "Application",
|
||||
|
||||
[Parameter(HelpMessage="Path to makepri.exe")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$MakePriPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\MakePri.exe"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "tmp$([Convert]::ToString((Get-Random 65535),16).PadLeft(4,'0')).tmp"
|
||||
New-Item -ItemType Directory -Path $tempDir | Out-Null
|
||||
$priConfig = Join-Path $tempDir "priconfig.xml"
|
||||
$priListFile = Join-Path $tempDir "pri.resfiles"
|
||||
$dumpListFile = Join-Path $tempDir "dump.resfiles"
|
||||
|
||||
@"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources targetOsVersion="10.0.0" majorVersion="1">
|
||||
<index root="\" startIndexAt="dump.resfiles">
|
||||
<default>
|
||||
<qualifier name="Language" value="en-US" />
|
||||
<qualifier name="Contrast" value="standard" />
|
||||
<qualifier name="Scale" value="200" />
|
||||
<qualifier name="HomeRegion" value="001" />
|
||||
<qualifier name="TargetSize" value="256" />
|
||||
<qualifier name="LayoutDirection" value="LTR" />
|
||||
<qualifier name="DXFeatureLevel" value="DX9" />
|
||||
<qualifier name="Configuration" value="" />
|
||||
<qualifier name="AlternateForm" value="" />
|
||||
<qualifier name="Platform" value="UAP" />
|
||||
</default>
|
||||
<indexer-config type="PRIINFO" />
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
<index root="\" startIndexAt="pri.resfiles">
|
||||
<default>
|
||||
<qualifier name="Language" value="en-US" />
|
||||
<qualifier name="Contrast" value="standard" />
|
||||
<qualifier name="Scale" value="200" />
|
||||
<qualifier name="HomeRegion" value="001" />
|
||||
<qualifier name="TargetSize" value="256" />
|
||||
<qualifier name="LayoutDirection" value="LTR" />
|
||||
<qualifier name="DXFeatureLevel" value="DX9" />
|
||||
<qualifier name="Configuration" value="" />
|
||||
<qualifier name="AlternateForm" value="" />
|
||||
<qualifier name="Platform" value="UAP" />
|
||||
</default>
|
||||
<indexer-config type="PRI" />
|
||||
<indexer-config type="RESFILES" qualifierDelimiter="." />
|
||||
</index>
|
||||
</resources>
|
||||
"@ | Out-File -Encoding:utf8NoBOM $priConfig
|
||||
|
||||
$Path | Where { $_ -Like "*.pri" } | ForEach-Object {
|
||||
Get-Item $_ | Select -Expand FullName
|
||||
} | Out-File -Encoding:utf8NoBOM $priListFile
|
||||
|
||||
$Path | Where { $_ -Like "*.xml" } | ForEach-Object {
|
||||
Get-Item $_ | Select -Expand FullName
|
||||
} | Out-File -Encoding:utf8NoBOM $dumpListFile
|
||||
|
||||
& $MakePriPath new /pr $tempDir /cf $priConfig /o /in $IndexName /of $OutputPath
|
||||
|
||||
Remove-Item -Recurse -Force $tempDir
|
||||
@@ -1,47 +0,0 @@
|
||||
Param(
|
||||
[Parameter(Mandatory,
|
||||
HelpMessage="Root directory of extracted Terminal AppX")]
|
||||
[string[]]
|
||||
$TerminalRoot,
|
||||
|
||||
[Parameter(Mandatory,
|
||||
HelpMessage="Root directory of extracted Xaml AppX")]
|
||||
[string[]]
|
||||
$XamlRoot,
|
||||
|
||||
[Parameter(Mandatory,
|
||||
HelpMessage="Output Path")]
|
||||
[string]
|
||||
$OutputPath,
|
||||
|
||||
[Parameter(HelpMessage="Path to makepri.exe")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$MakePriPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\MakePri.exe"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "tmp$([Convert]::ToString((Get-Random 65535),16).PadLeft(4,'0')).tmp"
|
||||
New-Item -ItemType Directory -Path $tempDir | Out-Null
|
||||
|
||||
$terminalDump = Join-Path $tempDir "terminal.pri.xml"
|
||||
|
||||
& $MakePriPath dump /if (Join-Path $TerminalRoot "resources.pri") /of $terminalDump /dt detailed
|
||||
|
||||
Write-Verbose "Removing Microsoft.UI.Xaml node from Terminal to prevent a collision with XAML"
|
||||
$terminalXMLDocument = [xml](Get-Content $terminalDump)
|
||||
$resourceMap = $terminalXMLDocument.PriInfo.ResourceMap
|
||||
$fileSubtree = $resourceMap.ResourceMapSubtree | Where-Object { $_.Name -eq "Files" }
|
||||
$subtrees = $fileSubtree.ResourceMapSubtree
|
||||
$xamlSubtreeChild = ($subtrees | Where-Object { $_.Name -eq "Microsoft.UI.Xaml" })
|
||||
if ($Null -Ne $xamlSubtreeChild) {
|
||||
$null = $fileSubtree.RemoveChild($xamlSubtreeChild)
|
||||
$terminalXMLDocument.Save($terminalDump)
|
||||
}
|
||||
|
||||
$indexName = $terminalXMLDocument.PriInfo.ResourceMap.name
|
||||
|
||||
& (Join-Path $PSScriptRoot "Merge-PriFiles.ps1") -Path $terminalDump, (Join-Path $XamlRoot "resources.pri") -IndexName $indexName -OutputPath $OutputPath -MakePriPath $MakePriPath
|
||||
|
||||
Remove-Item -Recurse -Force $tempDir
|
||||
@@ -1,140 +0,0 @@
|
||||
[CmdletBinding(DefaultParameterSetName = 'AppX')]
|
||||
Param(
|
||||
[Parameter(Mandatory, HelpMessage="Path to Terminal AppX", ParameterSetName = 'AppX')]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$TerminalAppX,
|
||||
|
||||
[Parameter(Mandatory, HelpMessage="Path to Terminal Layout Deployment", ParameterSetName='Layout')]
|
||||
[ValidateScript({Test-Path $_ -Type Container})]
|
||||
[string]
|
||||
$TerminalLayout,
|
||||
|
||||
[Parameter(Mandatory, HelpMessage="Path to Xaml AppX", ParameterSetName='AppX')]
|
||||
[Parameter(Mandatory, HelpMessage="Path to Xaml AppX", ParameterSetName='Layout')]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$XamlAppX,
|
||||
|
||||
[Parameter(HelpMessage="Output Directory", ParameterSetName='AppX')]
|
||||
[Parameter(HelpMessage="Output Directory", ParameterSetName='Layout')]
|
||||
[string]
|
||||
$Destination = ".",
|
||||
|
||||
[Parameter(HelpMessage="Path to makeappx.exe", ParameterSetName='AppX')]
|
||||
[Parameter(HelpMessage="Path to makeappx.exe", ParameterSetName='Layout')]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\MakeAppx.exe"
|
||||
)
|
||||
|
||||
$filesToRemove = @("*.xml", "*.winmd", "Appx*", "Images/*Tile*", "Images/*Logo*") # Remove from Terminal
|
||||
$filesToKeep = @("Microsoft.Terminal.Remoting.winmd") # ... except for these
|
||||
$filesToCopyFromXaml = @("Microsoft.UI.Xaml.dll", "Microsoft.UI.Xaml") # We don't need the .winmd
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
If ($null -Eq (Get-Item $MakeAppxPath -EA:SilentlyContinue)) {
|
||||
Write-Error "Could not find MakeAppx.exe at `"$MakeAppxPath`".`nMake sure that -MakeAppxPath points to a valid SDK."
|
||||
Exit 1
|
||||
}
|
||||
|
||||
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "tmp$([Convert]::ToString((Get-Random 65535),16).PadLeft(4,'0')).tmp"
|
||||
New-Item -ItemType Directory -Path $tempDir | Out-Null
|
||||
|
||||
$XamlAppX = Get-Item $XamlAppX | Select-Object -Expand FullName
|
||||
|
||||
########
|
||||
# Reading the AppX Manifest for preliminary info
|
||||
########
|
||||
|
||||
If ($TerminalAppX) {
|
||||
$appxManifestPath = Join-Path $tempDir AppxManifest.xml
|
||||
& tar.exe -x -f "$TerminalAppX" -C $tempDir AppxManifest.xml
|
||||
} ElseIf($TerminalLayout) {
|
||||
$appxManifestPath = Join-Path $TerminalLayout AppxManifest.xml
|
||||
}
|
||||
$manifest = [xml](Get-Content $appxManifestPath)
|
||||
$pfn = $manifest.Package.Identity.Name
|
||||
$version = $manifest.Package.Identity.Version
|
||||
$architecture = $manifest.Package.Identity.ProcessorArchitecture
|
||||
|
||||
$distributionName = "{0}_{1}_{2}" -f ($pfn, $version, $architecture)
|
||||
$terminalDir = "terminal-{0}" -f ($version)
|
||||
|
||||
########
|
||||
# Unpacking Terminal and XAML
|
||||
########
|
||||
|
||||
$terminalAppPath = Join-Path $tempdir $terminalDir
|
||||
|
||||
If ($TerminalAppX) {
|
||||
$TerminalAppX = Get-Item $TerminalAppX | Select-Object -Expand FullName
|
||||
New-Item -ItemType Directory -Path $terminalAppPath | Out-Null
|
||||
& $MakeAppxPath unpack /p $TerminalAppX /d $terminalAppPath /o | Out-Null
|
||||
If ($LASTEXITCODE -Ne 0) {
|
||||
Throw "Unpacking $TerminalAppX failed"
|
||||
}
|
||||
} ElseIf ($TerminalLayout) {
|
||||
Copy-Item -Recurse -Path $TerminalLayout -Destination $terminalAppPath
|
||||
}
|
||||
|
||||
$xamlAppPath = Join-Path $tempdir "xaml"
|
||||
New-Item -ItemType Directory -Path $xamlAppPath | Out-Null
|
||||
& $MakeAppxPath unpack /p $XamlAppX /d $xamlAppPath /o | Out-Null
|
||||
If ($LASTEXITCODE -Ne 0) {
|
||||
Throw "Unpacking $XamlAppX failed"
|
||||
}
|
||||
|
||||
########
|
||||
# Some sanity checking
|
||||
########
|
||||
|
||||
$xamlManifest = [xml](Get-Content (Join-Path $xamlAppPath "AppxManifest.xml"))
|
||||
If ($xamlManifest.Package.Identity.Name -NotLike "Microsoft.UI.Xaml*") {
|
||||
Throw "$XamlAppX is not a XAML package (instead, it looks like $($xamlManifest.Package.Identity.Name))"
|
||||
}
|
||||
If ($xamlManifest.Package.Identity.ProcessorArchitecture -Ne $architecture) {
|
||||
Throw "$XamlAppX is not built for $architecture (instead, it is built for $($xamlManifest.Package.Identity.ProcessorArchitecture))"
|
||||
}
|
||||
|
||||
########
|
||||
# Preparation of source files
|
||||
########
|
||||
|
||||
$itemsToRemove = $filesToRemove | ForEach-Object {
|
||||
Get-Item (Join-Path $terminalAppPath $_) -EA:SilentlyContinue | Where-Object {
|
||||
$filesToKeep -NotContains $_.Name
|
||||
}
|
||||
} | Sort-Object FullName -Unique
|
||||
$itemsToRemove | Remove-Item -Recurse
|
||||
|
||||
$filesToCopyFromXaml | ForEach-Object {
|
||||
Get-Item (Join-Path $xamlAppPath $_)
|
||||
} | Copy-Item -Recurse -Destination $terminalAppPath
|
||||
|
||||
########
|
||||
# Resource Management
|
||||
########
|
||||
|
||||
$finalTerminalPriFile = Join-Path $terminalAppPath "resources.pri"
|
||||
& (Join-Path $PSScriptRoot "Merge-TerminalAndXamlResources.ps1") `
|
||||
-TerminalRoot $terminalAppPath `
|
||||
-XamlRoot $xamlAppPath `
|
||||
-OutputPath $finalTerminalPriFile `
|
||||
-Verbose:$Verbose | Out-Host
|
||||
|
||||
########
|
||||
# Packaging
|
||||
########
|
||||
|
||||
If ($PSCmdlet.ParameterSetName -Eq "AppX") {
|
||||
# We only produce a ZIP when we're combining two AppX directories.
|
||||
New-Item -ItemType Directory -Path $Destination -ErrorAction:SilentlyContinue | Out-Null
|
||||
$outputZip = (Join-Path $Destination ("{0}.zip" -f ($distributionName)))
|
||||
& tar -c --format=zip -f $outputZip -C $tempDir $terminalDir
|
||||
Remove-Item -Recurse -Force $tempDir -EA:SilentlyContinue
|
||||
Get-Item $outputZip
|
||||
} ElseIf ($PSCmdlet.ParameterSetName -Eq "Layout") {
|
||||
Get-Item $terminalAppPath
|
||||
}
|
||||
14
build/scripts/Patch-ManifestsToWindowsVersion.ps1
Normal file
14
build/scripts/Patch-ManifestsToWindowsVersion.ps1
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT license.
|
||||
|
||||
Param(
|
||||
[string]$NewWindowsVersion = "10.0.22000.0"
|
||||
)
|
||||
|
||||
Get-ChildItem src/cascadia/CascadiaPackage -Recurse -Filter *.appxmanifest | ForEach-Object {
|
||||
$xml = [xml](Get-Content $_.FullName)
|
||||
$xml.Package.Dependencies.TargetDeviceFamily | Where-Object Name -Like "Windows*" | ForEach-Object {
|
||||
$_.MinVersion = $NewWindowsVersion
|
||||
}
|
||||
$xml.Save($_.FullName)
|
||||
}
|
||||
@@ -1,36 +1,27 @@
|
||||
[CmdLetBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true, Position=0)]
|
||||
[string]$MatchPattern,
|
||||
[Parameter(Mandatory=$true, Position=1)]
|
||||
[string]$Platform,
|
||||
[Parameter(Mandatory=$true, Position=2)]
|
||||
[string]$Configuration,
|
||||
[Parameter(Mandatory=$false, Position=3)]
|
||||
[string]$LogPath,
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Root = ".\bin\$Platform\$Configuration"
|
||||
[Parameter(Mandatory=$true, Position=0)][string]$MatchPattern,
|
||||
[Parameter(Mandatory=$true, Position=1)][string]$Platform,
|
||||
[Parameter(Mandatory=$true, Position=2)][string]$Configuration,
|
||||
[Parameter(Mandatory=$false, Position=3)][string]$LogPath,
|
||||
[Parameter(Mandatory=$false)][string]$Root = ".\bin\$Platform\$Configuration"
|
||||
)
|
||||
|
||||
# Find test DLLs based on the provided root, match pattern, and recursion
|
||||
$testDlls = Get-ChildItem -Path $Root -Recurse -Filter $MatchPattern
|
||||
$testdlls = Get-ChildItem -Path "$Root" -Recurse -Filter $MatchPattern
|
||||
|
||||
$args = @()
|
||||
|
||||
# Check if the LogPath parameter is provided and enable WTT logging
|
||||
if ($LogPath) {
|
||||
$args += '/enablewttlogging'
|
||||
$args += '/appendwttlogging'
|
||||
$args += "/logFile:$LogPath"
|
||||
Write-Host "WTT Logging Enabled"
|
||||
$args = @();
|
||||
|
||||
if ($LogPath)
|
||||
{
|
||||
$args += '/enablewttlogging';
|
||||
$args += '/appendwttlogging';
|
||||
$args += "/logFile:$LogPath";
|
||||
Write-Host "Wtt Logging Enabled";
|
||||
}
|
||||
|
||||
# Invoke the te.exe executable with arguments and test DLLs
|
||||
& "$Root\te.exe" $args $testDlls.FullName
|
||||
&"$Root\te.exe" $args $testdlls.FullName
|
||||
|
||||
# Check the exit code of the te.exe process and exit accordingly
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Exit $LASTEXITCODE
|
||||
}
|
||||
if ($lastexitcode -Ne 0) { Exit $lastexitcode }
|
||||
|
||||
Exit 0
|
||||
|
||||
@@ -70,24 +70,23 @@ Try {
|
||||
|
||||
$dependencies = $Manifest.Package.Dependencies.PackageDependency.Name
|
||||
$depsHasVclibsDesktop = ("Microsoft.VCLibs.140.00.UWPDesktop" -in $dependencies) -or ("Microsoft.VCLibs.140.00.Debug.UWPDesktop" -in $dependencies)
|
||||
$depsHasVclibsAppX = ("Microsoft.VCLibs.140.00" -in $dependencies) -or ("Microsoft.VCLibs.140.00.Debug" -in $dependencies)
|
||||
$depsHasVcLibsAppX = ("Microsoft.VCLibs.140.00" -in $dependencies) -or ("Microsoft.VCLibs.140.00.Debug" -in $dependencies)
|
||||
$filesHasVclibsDesktop = ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140.dll" -EA:Ignore)) -or ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140d.dll" -EA:Ignore))
|
||||
$filesHasVclibsAppX = ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140_app.dll" -EA:Ignore)) -or ($null -ne (Get-Item "$AppxPackageRootPath\vcruntime140d_app.dll" -EA:Ignore))
|
||||
|
||||
If ($filesHasVclibsDesktop) {
|
||||
Throw "Package contains the desktop VCLibs"
|
||||
If ($depsHasVclibsDesktop -Eq $filesHasVclibsDesktop) {
|
||||
$eitherBoth = if ($depsHasVclibsDesktop) { "both" } else { "neither" }
|
||||
$neitherNor = if ($depsHasVclibsDesktop) { "and" } else { "nor" }
|
||||
Throw "Package has $eitherBoth Dependency $neitherNor Integrated Desktop VCLibs"
|
||||
}
|
||||
|
||||
If ($depsHasVclibsDesktop) {
|
||||
Throw "Package has a dependency on the desktop VCLibs"
|
||||
}
|
||||
|
||||
If ($filesHasVclibsAppX) {
|
||||
Throw "Package contains the AppX VCLibs"
|
||||
}
|
||||
|
||||
If ($depsHasVclibsAppX) {
|
||||
Throw "Package has a dependency on the AppX VCLibs"
|
||||
If ($depsHasVclibsAppx -Eq $filesHasVclibsAppx) {
|
||||
if ($depsHasVclibsAppx) {
|
||||
# We've shipped like this forever, so downgrade to warning.
|
||||
Write-Warning "Package has both Dependency and Integrated AppX VCLibs"
|
||||
} else {
|
||||
Throw "Package has neither Dependency nor Integrated AppX VCLibs"
|
||||
}
|
||||
}
|
||||
|
||||
### Check that we have an App.xbf (which is a proxy for our resources having been merged)
|
||||
@@ -97,6 +96,11 @@ Try {
|
||||
Throw "Failed to find App.xbf (TerminalApp project) in resources.pri"
|
||||
}
|
||||
|
||||
If (($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10.dll" -EA:Ignore)) -And
|
||||
($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10d.dll" -EA:Ignore))) {
|
||||
Throw "Failed to find cpprest142_2_10.dll -- check the WAP packaging project"
|
||||
}
|
||||
|
||||
If (($null -eq (Get-Item "$AppxPackageRootPath\wtd.exe" -EA:Ignore)) -And
|
||||
($null -eq (Get-Item "$AppxPackageRootPath\wt.exe" -EA:Ignore))) {
|
||||
Throw "Failed to find wt.exe/wtd.exe -- check the WAP packaging project"
|
||||
|
||||
@@ -10,4 +10,22 @@
|
||||
<OpenConsoleDir>$(MSBuildThisFileDirectory)</OpenConsoleDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
For the Windows 10 build, we're targeting the prerelease version of Microsoft.UI.Xaml.
|
||||
This version emits every XAML DLL directly into our package.
|
||||
This is a workaround for us not having deliverable MSFT-21242953 on this version of Windows.
|
||||
|
||||
This version should be tracked in all project packages.config files for projects that depend on Xaml.
|
||||
-->
|
||||
<TerminalMUXVersion>2.7.3-prerelease.220816001</TerminalMUXVersion>
|
||||
<!--
|
||||
For the Windows 11-specific build, we're targeting the public version of Microsoft.UI.Xaml.
|
||||
This version emits a package dependency instead of embedding the dependency in our own package.
|
||||
|
||||
This version should be tracked in build/packages.config.
|
||||
-->
|
||||
<TerminalMUXVersion Condition="'$(TerminalTargetWindowsVersion)'=='Win11'">2.7.3</TerminalMUXVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
14
custom.props
14
custom.props
@@ -2,10 +2,22 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- This file is read by XES, which we use in our Release builds. -->
|
||||
<PropertyGroup Label="Version">
|
||||
<!--
|
||||
The Windows 11 build is going to have the same package name, so it *must* have a different version.
|
||||
The easiest way for us to do this is to add 1 to the revision field.
|
||||
In short, for a given Terminal build 1.11, we will emit two different versions (assume this is build
|
||||
4 on day 23 of the year):
|
||||
- 1.11.234.0 for Windows 10
|
||||
- 1.11.235.0 for Windows 11
|
||||
This presents a potential for conflicts if we want to ship two builds produced back to back on the
|
||||
same day... which is terribly unlikely.
|
||||
-->
|
||||
<VersionBuildRevision Condition="'$(TerminalTargetWindowsVersion)'=='Win11' and '$(VersionBuildRevision)'!=''">$([MSBuild]::Add($(VersionBuildRevision), 1))</VersionBuildRevision>
|
||||
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2023</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>19</VersionMinor>
|
||||
<VersionMinor>18</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
<!-- Native packages -->
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.7.230706001" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
<package id="vcpkg-cpprestsdk" version="2.10.14" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.6.220404001" targetFramework="native" />
|
||||
<package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.8.4" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.1661.34" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3-prerelease.220816001" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.220201.1" targetFramework="native" developmentDependency="true" />
|
||||
|
||||
<!-- Managed packages -->
|
||||
|
||||
@@ -24,13 +24,6 @@
|
||||
"pattern": "^(-?\\d+)?(,\\s?(-?\\d+)?)?$",
|
||||
"type": "string"
|
||||
},
|
||||
"CSSLengthPercentage": {
|
||||
"pattern": "^[+-]?\\d+(?:\\.\\d+)?(?:%|ch|pt|px)?$",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"DynamicProfileSource": {
|
||||
"enum": [
|
||||
"Windows.Terminal.Wsl",
|
||||
@@ -321,14 +314,6 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"cellWidth": {
|
||||
"$ref": "#/$defs/CSSLengthPercentage",
|
||||
"description": "Override the width of the terminal's cells. The override works similar to CSS' letter-spacing. It defaults to the natural glyph advance width of the primary font rounded to the nearest pixel."
|
||||
},
|
||||
"cellHeight": {
|
||||
"$ref": "#/$defs/CSSLengthPercentage",
|
||||
"description": "Override the height of the terminal's cells. The override works similar to CSS' line-height. Defaults to the sum of the natural glyph ascend, descend and line-gap of the primary font rounded to the nearest pixel. The default is usually quite close to setting this to 1.2."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -421,7 +406,6 @@
|
||||
"scrollToMark",
|
||||
"clearMark",
|
||||
"clearAllMarks",
|
||||
"searchWeb",
|
||||
"experimental.colorSelection",
|
||||
"unbound"
|
||||
],
|
||||
@@ -799,11 +783,6 @@
|
||||
"default": false,
|
||||
"description": "If true, the copied content will be copied as a single line (even if there are hard line breaks present in the text). If false, newlines persist from the selected text."
|
||||
},
|
||||
"dismissSelection": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "If false, the copied content will be copied and the selection will not be dismissed. If true, the selection will be dismissed."
|
||||
},
|
||||
"copyFormatting": {
|
||||
"default": null,
|
||||
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied. Not setting this value inherits the behavior of the `copyFormatting` global setting.",
|
||||
@@ -1715,29 +1694,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"SearchWebAction": {
|
||||
"description": "Search the web for selected text",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ShortcutAction"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"const": "searchWeb"
|
||||
},
|
||||
"queryUrl": {
|
||||
"type": "string",
|
||||
"description": "URL of the web page to launch, %s is replaced with the selected text"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [
|
||||
"queryUrl"
|
||||
]
|
||||
},
|
||||
"AdjustOpacityAction": {
|
||||
"description": "Changes the opacity of the active Terminal window. If `relative` is specified, then this action will increase/decrease relative to the current opacity.",
|
||||
"allOf": [
|
||||
@@ -1770,8 +1726,7 @@
|
||||
"enum": [
|
||||
"always",
|
||||
"hover",
|
||||
"never",
|
||||
"activeOnly"
|
||||
"never"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1842,19 +1797,6 @@
|
||||
"description": "True if the Terminal should use a Mica backdrop for the window. This will apply underneath all controls (including the terminal panes and the titlebar)",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"experimental.rainbowFrame": {
|
||||
"description": "When enabled, the frame of the window will cycle through all the colors. Enabling this will override the `frame` and `unfocusedFrame` settings.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"frame": {
|
||||
"description": "The color of the window frame when the window is inactive. This only works on Windows 11",
|
||||
"$ref": "#/$defs/ThemeColor"
|
||||
},
|
||||
"unfocusedFrame": {
|
||||
"description": "The color of the window frame when the window is inactive. This only works on Windows 11",
|
||||
"$ref": "#/$defs/ThemeColor"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1865,7 +1807,7 @@
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the theme. This will be displayed in the settings UI.",
|
||||
"not": {
|
||||
"not": {
|
||||
"enum": [ "light", "dark", "system" ]
|
||||
}
|
||||
},
|
||||
@@ -2033,9 +1975,6 @@
|
||||
{
|
||||
"$ref": "#/$defs/ColorSelectionAction"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/SearchWebAction"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
@@ -2138,11 +2077,6 @@
|
||||
"description": "When set to true, the terminal will focus the pane on mouse hover.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"compatibility.isolatedMode": {
|
||||
"default": false,
|
||||
"description": "When set to true, Terminal windows will not be able to interact with each other (including global hotkeys, tab drag/drop, running commandlines in existing windows, etc.). This is a compatibility escape hatch for users who are running into certain windowing issues.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyFormatting": {
|
||||
"default": true,
|
||||
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.",
|
||||
@@ -2215,11 +2149,6 @@
|
||||
"description": "When set to true, the background image for the currently focused profile is expanded to encompass the entire window, beneath other panes.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"compatibility.reloadEnvironmentVariables": {
|
||||
"default": true,
|
||||
"description": "When set to true, when opening a new tab or pane it will get reloaded environment variables.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"initialCols": {
|
||||
"default": 120,
|
||||
"description": "The number of columns displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",
|
||||
@@ -2620,13 +2549,6 @@
|
||||
"default": false,
|
||||
"description": "When true, this profile should always open in an elevated context. If the window isn't running as an Administrator, then a new elevated window will be created."
|
||||
},
|
||||
"environment": {
|
||||
"description": "Key-value pairs representing environment variables to set. Environment variable names are not case sensitive. You can reference existing environment variable names by enclosing them in literal percent characters (e.g. %PATH%).",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"experimental.autoMarkPrompts": {
|
||||
"default": false,
|
||||
"description": "When set to true, prompts will automatically be marked.",
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Perceptual Color Nudging</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<style>
|
||||
html {
|
||||
background-color: #0c0c0c;
|
||||
color: #cccccc;
|
||||
font-family: "Cascadia Code", "Cascadia Mono", monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
form,
|
||||
h2 {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
p,
|
||||
pre {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="flex: 2; align-items: flex-start; background-color: #0c0c0c">
|
||||
<form>
|
||||
<input id="background-color" name="background-color" type="color" value="#0c0c0c" />
|
||||
<label for="background-color">background color</label>
|
||||
</form>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Input</td>
|
||||
<td>WCAG21:<br>APCA:</td>
|
||||
<td id="stats-input"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ΔE2000<br>(ConEmu)</td>
|
||||
<td>WCAG21:<br>APCA:</td>
|
||||
<td id="stats-cielab"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ΔEOK</td>
|
||||
<td>WCAG21:<br>APCA:</td>
|
||||
<td id="stats-oklab"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="input" style="flex: 1">
|
||||
<h2>Input</h2>
|
||||
<pre style="font-size: 8pt">猫</pre>
|
||||
<pre style="font-size: 10pt">猫</pre>
|
||||
<pre style="font-size: 12pt">猫</pre>
|
||||
<pre style="font-size: 14pt">猫</pre>
|
||||
<pre style="font-size: 16pt">猫</pre>
|
||||
<pre style="font-size: 32pt">猫</pre>
|
||||
<pre style="font-size: 64pt">猫</pre>
|
||||
</div>
|
||||
<div id="cielab" style="flex: 1">
|
||||
<h2>ΔE2000 (ConEmu)</h2>
|
||||
<pre style="font-size: 8pt">猫</pre>
|
||||
<pre style="font-size: 10pt">猫</pre>
|
||||
<pre style="font-size: 12pt">猫</pre>
|
||||
<pre style="font-size: 14pt">猫</pre>
|
||||
<pre style="font-size: 16pt">猫</pre>
|
||||
<pre style="font-size: 32pt">猫</pre>
|
||||
<pre style="font-size: 64pt">猫</pre>
|
||||
</div>
|
||||
<div id="oklab" style="flex: 1">
|
||||
<h2>ΔEOK</h2>
|
||||
<pre style="font-size: 8pt">猫</pre>
|
||||
<pre style="font-size: 10pt">猫</pre>
|
||||
<pre style="font-size: 12pt">猫</pre>
|
||||
<pre style="font-size: 14pt">猫</pre>
|
||||
<pre style="font-size: 16pt">猫</pre>
|
||||
<pre style="font-size: 32pt">猫</pre>
|
||||
<pre style="font-size: 64pt">猫</pre>
|
||||
</div>
|
||||
<script type="module">
|
||||
import Color from "https://cdn.jsdelivr.net/npm/colorjs.io@0.4.3/+esm";
|
||||
|
||||
window.Color = Color;
|
||||
|
||||
const input = document.getElementById("input");
|
||||
const cielab = document.getElementById("cielab");
|
||||
const oklab = document.getElementById("oklab");
|
||||
|
||||
const statsInput = document.getElementById("stats-input");
|
||||
const statsCielab = document.getElementById("stats-cielab");
|
||||
const statsOklab = document.getElementById("stats-oklab");
|
||||
|
||||
let backgroundColor = new Color("#0c0c0c");
|
||||
let foregroundColor = new Color("#0c0c0c");
|
||||
let foregroundColorRange = null;
|
||||
let previousSecsIntegral = -1;
|
||||
|
||||
function saturate(val) {
|
||||
return val < 0 ? 0 : val > 1 ? 1 : val;
|
||||
}
|
||||
|
||||
function clipToSrgb(color) {
|
||||
return color.to("srgb").toGamut({ method: "clip" });
|
||||
}
|
||||
|
||||
function nudgeCielab(backgroundColor, foregroundColor) {
|
||||
const backgroundCielab = backgroundColor.to("lab-d65");
|
||||
const foregroundCielab = foregroundColor.to("lab-d65");
|
||||
|
||||
const de1 = Color.deltaE(foregroundColor, backgroundCielab, "2000");
|
||||
if (de1 >= 12.0) {
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 1; i++) {
|
||||
const step = (i == 0) ? 5.0 : -5.0;
|
||||
foregroundCielab.l += step;
|
||||
|
||||
while (((i == 0) && foregroundCielab.l <= 100) || (i == 1 && foregroundCielab.l >= 0)) {
|
||||
const de2 = Color.deltaE(foregroundCielab, backgroundCielab, "2000");
|
||||
if (de2 >= 20.0) {
|
||||
return clipToSrgb(foregroundCielab);
|
||||
}
|
||||
foregroundCielab.l += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function nudgeOklab(backgroundColor, foregroundColor) {
|
||||
const backgroundOklab = backgroundColor.to("oklab");
|
||||
const foregroundOklab = foregroundColor.to("oklab");
|
||||
const deltaSquared = {
|
||||
l: (backgroundOklab.l - foregroundOklab.l) ** 2,
|
||||
a: (backgroundOklab.a - foregroundOklab.a) ** 2,
|
||||
b: (backgroundOklab.b - foregroundOklab.b) ** 2,
|
||||
};
|
||||
const distance = deltaSquared.l + deltaSquared.a + deltaSquared.b;
|
||||
|
||||
if (distance >= 0.25) {
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
let deltaL = Math.sqrt(0.25 - deltaSquared.a - deltaSquared.b);
|
||||
if (foregroundOklab.l < backgroundOklab.l)
|
||||
{
|
||||
deltaL = -deltaL;
|
||||
}
|
||||
|
||||
foregroundOklab.l = backgroundOklab.l + deltaL;
|
||||
if (foregroundOklab.l < 0 || foregroundOklab.l > 1)
|
||||
{
|
||||
foregroundOklab.l = backgroundOklab.l - deltaL;
|
||||
}
|
||||
|
||||
return clipToSrgb(foregroundOklab);
|
||||
}
|
||||
|
||||
function contrastStringLevels(num, level0, level1) {
|
||||
const str = num.toFixed(1);
|
||||
if (num < level0) {
|
||||
return `<span style="color:crimson">${str}</span>`;
|
||||
}
|
||||
if (num < level1) {
|
||||
return `<span style="color:coral">${str}</span>`;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function contrastString(foregroundColor) {
|
||||
const contrastWCAG21 = contrastStringLevels(foregroundColor.contrast(backgroundColor, "WCAG21"), 3, 4.5);
|
||||
const contrastAPCA = contrastStringLevels(Math.abs(foregroundColor.contrast(backgroundColor, "APCA")), 45, 60);
|
||||
return `${contrastWCAG21}<br/>${contrastAPCA}`;
|
||||
}
|
||||
|
||||
function animate(time) {
|
||||
const timeScale = time / 1000;
|
||||
const secsIntegral = Math.trunc(timeScale);
|
||||
const secsFractional = timeScale % 1;
|
||||
|
||||
if (previousSecsIntegral != secsIntegral) {
|
||||
const foregroundColorTarget = new Color("srgb", backgroundColor.coords.map(c => saturate(c + Math.random() - 0.5)));
|
||||
foregroundColorRange = foregroundColor.range(foregroundColorTarget, { space: "srgb" });
|
||||
previousSecsIntegral = secsIntegral;
|
||||
}
|
||||
|
||||
foregroundColor = foregroundColorRange(secsFractional);
|
||||
input.style.color = foregroundColor.toString({ inGamut: false });
|
||||
|
||||
const foregroundCielabNudged = nudgeCielab(backgroundColor, foregroundColor);
|
||||
const foregroundOklabNudged = nudgeOklab(backgroundColor, foregroundColor);
|
||||
|
||||
cielab.style.color = foregroundCielabNudged;
|
||||
oklab.style.color = foregroundOklabNudged;
|
||||
|
||||
statsInput.innerHTML = contrastString(foregroundColor);
|
||||
statsCielab.innerHTML = contrastString(foregroundCielabNudged);
|
||||
statsOklab.innerHTML = contrastString(foregroundOklabNudged);
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
document.getElementById("background-color").addEventListener("input", event => {
|
||||
backgroundColor = new Color(event.target.value);
|
||||
document.documentElement.style.backgroundColor = backgroundColor;
|
||||
}, false);
|
||||
|
||||
document.documentElement.style.backgroundColor = backgroundColor;
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,601 +0,0 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2022-09-07
|
||||
last updated: 2023-07-12
|
||||
issue id: 5916
|
||||
---
|
||||
|
||||
# Triggers and Custom Clickable Links
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec outlines a mechanism by which users can define custom actions to run
|
||||
when a string of text is written to the Terminal. This lets users create
|
||||
powerful ways of automating their Terminal to match their own workflows. This
|
||||
same mechanism can be used by third-party applications to customize the way the
|
||||
terminal control automatically identifies links or other clickable regions of
|
||||
the buffer, and handle it in their own way.
|
||||
|
||||
## Background
|
||||
|
||||
### Inspiration
|
||||
|
||||
Much of this was heavily inspired by the [VsCode "auto replies"], as well as the
|
||||
[triggers in iTerm2]. VsCode is also working on a draft "[Quick Fix API]", from
|
||||
which a lot of inspiration was taken as far as crafting the `match` syntax. You
|
||||
can seen a sample of their syntax
|
||||
[here](https://github.com/microsoft/vscode/blob/4c6e0b5eee419550e31e386a0a5ca9840ac3280b/extensions/npm/package.json#L342-L354).
|
||||
|
||||
### User Stories
|
||||
|
||||
This is a collection of all the possible issues I found on this topic.
|
||||
|
||||
* [x] [#5916] Triggers(Including Text Strings) and Actions (internal or external calls)
|
||||
* [x] [#8849] Allow "hyperlink" matching for arbitrary patterns and adding custom handlers
|
||||
* [x] [#7562] Allow more URI schemes
|
||||
* [x] [#2671] Feature Request: link generation for files + other data types
|
||||
- This is a dupe of some of the above, honestly. Either [#5916] or [#8849]
|
||||
* [x] [#6969] Let terminal consumers provide click handlers and pattern recognizers for buffer text
|
||||
* [x] [#11901] IPv6 links can not be Ctrl+Clicked
|
||||
* [x] Probably don't do this for the alt buffer? Make it per-trigger if they work in the alt buffer?
|
||||
|
||||
Not addressed as a part of this spec:
|
||||
|
||||
* [ ] [#8294] Add a setting to configure display of auto detected links, normally and on hover
|
||||
|
||||
We'll probably want to come back and separately spec how users can control the
|
||||
appearance of both auto-detected clickable text, and links that are manually
|
||||
emitted to the Terminal (via OSC 8). This may be increasingly relevant, as this
|
||||
spec will introduce new ways for users to make text clickable.
|
||||
|
||||
## UX / UI Design
|
||||
|
||||
These are some prototypes from initial investigations in the space. The settings
|
||||
in these are not up to the current spec.
|
||||
|
||||

|
||||
|
||||
_fig 1: a `sendInput` trigger that sends "y" any time that "Terminate batch job" is written to the Terminal_
|
||||
|
||||
|
||||

|
||||
|
||||
_fig 2: A trigger that adds a red error mark whenever an app writes "error" to the Terminal_
|
||||
|
||||
|
||||

|
||||
|
||||
_fig 3: A sendInput trigger which sends some of the matched text_
|
||||
|
||||
|
||||
## Solution Design
|
||||
|
||||
To support "triggers", we'll add a new object to the profile settings called
|
||||
`triggers`. This is an array of objects that describe
|
||||
|
||||
1. What string to match on for running the trigger
|
||||
2. What to do when that match is found.
|
||||
|
||||
Let's start with an example blob of JSON:
|
||||
|
||||
```jsonc
|
||||
"triggers":[
|
||||
{
|
||||
"match": "[Ee][Rr][Rr][Oo][Rr]",
|
||||
"action": "addMark",
|
||||
"color": "#ff0000"
|
||||
},
|
||||
{
|
||||
"match": "Terminate batch job \\(Y/N\\)",
|
||||
"action": "sendInput",
|
||||
"input": "y\r\n"
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"anchor": "bottom",
|
||||
"offset": 4,
|
||||
"length": 5,
|
||||
"pattern": " git push --set-upstream origin ([^\\w]*)",
|
||||
"runOn": "mark"
|
||||
},
|
||||
"action": "sendInput",
|
||||
"input": "git push --set-upstream origin ${match[1]}\r"
|
||||
},
|
||||
{
|
||||
"match": "'(.*)' is not recognized as an internal or external command,",
|
||||
"action": "checkWinGet",
|
||||
"commandline": "${match[1]}"
|
||||
},
|
||||
{
|
||||
"match": "This will open a new Terminal tab",
|
||||
"action": "newTab"
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
`triggers` is an array of `Trigger` objects, which are a combination of an
|
||||
`ActionAndArgs`, a `Control.Matcher`, and a `Control.TriggerAction`.
|
||||
|
||||
> **Note**
|
||||
> Interface and other types here are shown in C#, since that's fairly close to
|
||||
> MIDL3, which would be used for cross-component interfaces.
|
||||
|
||||
```c#
|
||||
runtimeclass Terminal.Settings.Model.Trigger
|
||||
{
|
||||
String Id;
|
||||
|
||||
Control.Matcher Matcher;
|
||||
Control.TriggerAction ControlAction;
|
||||
|
||||
Terminal.Settings.Model.ActionAndArgs ParseMatches(String[] matches);
|
||||
|
||||
private String _actionJson;
|
||||
}
|
||||
```
|
||||
|
||||
### Trigger matchers
|
||||
|
||||
The `match` property is parsed as a `Control.Matcher`. This includes the
|
||||
following properties, which are heavily inspired by the matchers in VsCode.
|
||||
|
||||
```jsonc
|
||||
"match": {
|
||||
"anchor": "bottom",
|
||||
"offset": 0, // starting how many rows from the bottom
|
||||
"length": 5, // how many lines to match against
|
||||
"pattern": " git push --set-upstream origin ([^\\w]*)",
|
||||
"runOn": "everything|newline|mark",
|
||||
"buffer": "main|alt|any"
|
||||
}
|
||||
```
|
||||
|
||||
* `"anchor"` (_default: `"bottom"`_) : Which part of the viewport to run the match against.
|
||||
- VsCode has this, but I don't think we really do. I think we can just always say bottom.
|
||||
* `"offset"` (_default: `0`_) : Run the match starting how many rows from the anchor
|
||||
* `"length"` (_default: `1`_) : How many rows to include in the match
|
||||
* `"pattern"` (_required_) : The regex to run. If omitted / empty, this entire
|
||||
trigger is ignored.
|
||||
* `"runOn"` (_default: `"newline"`_) : When to run this match.
|
||||
* `"everything"`: On every print from the connection. High performance impact.
|
||||
* `"newline"`: On every newline character from the connection. Medium
|
||||
performance impact, depending on the workload.
|
||||
* `"mark"`: Whenever a mark ([#11000]) is emitted to the terminal. With
|
||||
`autoMarkPrompts`, this includes when the user presses <kbd>enter</kbd>.
|
||||
This has a much smaller performance impact, assuming the user isn't marking
|
||||
every line.
|
||||
* `"buffer"` (_default_: `"any"`): Configure which terminal buffer this trigger runs on.
|
||||
* `main`: only run in the main buffer (e.g., most shells)
|
||||
* `alt`: only run in the alternate screen buffer (e.g., `vim`, `tmux`, any full-screen application)
|
||||
* `any`: Run in either buffer.
|
||||
|
||||
`match` also accepts just a single string, to automatically assume default
|
||||
values for the other properties. For example, the JSON `"match":
|
||||
"[Ee][Rr][Rr][Oo][Rr]"` is evaluated as
|
||||
|
||||
```jsonc
|
||||
"match": {
|
||||
"anchor": "bottom",
|
||||
"offset": 0,
|
||||
"length": 1,
|
||||
"pattern": "[Ee][Rr][Rr][Oo][Rr]",
|
||||
"runOn": "newline"
|
||||
}
|
||||
```
|
||||
|
||||
VsCode only runs matchers on prompt sequences, for performance reasons. We'll
|
||||
need to be VERY explicit that anything else is highly detrimental to
|
||||
performance. We really don't want these to necessarily be running on every
|
||||
single character the connection emits.
|
||||
|
||||
Marks are preferable, because with shell integration, they'll be emitted on
|
||||
every prompt from the shell. Prompts are probably close enough to frequent
|
||||
enough, and the anchor/offset/length give a subset of the buffer that can be
|
||||
used to run matches on.
|
||||
|
||||
### Trigger actions
|
||||
|
||||
Triggers also contain the action that should be performed when a match is found.
|
||||
These actions are used internally by the control as a `Control.TriggerAction`.
|
||||
This allows the control to perform the action itself without going out to the
|
||||
app every time a match is found.
|
||||
|
||||
```c#
|
||||
enum Terminal.Control.TriggerType {
|
||||
CustomAction,
|
||||
|
||||
AddMark,
|
||||
SendInput,
|
||||
ColorSelection,
|
||||
ClickableLink,
|
||||
ClickableSendInput,
|
||||
}
|
||||
|
||||
runtimeclass Terminal.Control.TriggerAction
|
||||
{
|
||||
TriggerType Type;
|
||||
TriggerArgs Args;
|
||||
internal void Execute(ControlCore core, String[] matches);
|
||||
}
|
||||
|
||||
runtimeclass Terminal.Control.TriggerAndMatch
|
||||
{
|
||||
Control.Matcher Matcher;
|
||||
Control.TriggerAction Action;
|
||||
}
|
||||
|
||||
runtimeclass Terminal.Control.CustomActionArgs
|
||||
{
|
||||
UInt32 Index;
|
||||
String[] matches;
|
||||
}
|
||||
```
|
||||
|
||||
There are some built-in `Control.TriggerAction`s (which largely align with
|
||||
existing `ShortcutAction`s). The supported types of `Control.TriggerAction` the
|
||||
user can specify are:
|
||||
|
||||
* `addMark`: Basically the same as the `addMark` action
|
||||
* `sendInput`: Basically the same as the `sendInput` action
|
||||
* `experimental.colorSelection`: Basically the same as the `sendInput` action
|
||||
* `clickableLink`: Turn the matched text into a clickable link. When the user
|
||||
clicks that text, we'll attempt to `ShellExecute` that link, much like a URL.
|
||||
* `clickableSendInput`: Similarly, turn the matched text into a clickable region
|
||||
which, when clicked, sends that text to the connection as input.
|
||||
|
||||
However, if the JSON of the trigger doesn't evaluate as a
|
||||
`Control.TriggerAction`, we'll instead try to parse the json as a
|
||||
`ActionAndArgs`. If we find that, then we'll use a special
|
||||
`Control.TriggerAction` type:
|
||||
|
||||
* `customAction`: This is an internal `TriggerAction` type. This is used to
|
||||
allow the app to provide its own action handler. The control will raise a
|
||||
`CustomAction` event, with args containing the index of the trigger that
|
||||
matched, and the matching groups of the regex.
|
||||
|
||||
`Control.Trigger`'s, when they are hit by the control, will be able to string
|
||||
replace their args themselves. For example:
|
||||
* The `git push` trigger above - when matched, the Core will call
|
||||
`Control.SendInputTrigger.Execute(ControlCore core, String[] matches)` (or
|
||||
something like that). `Execute` will be responsible for string-replacing
|
||||
anything it needs to.
|
||||
|
||||
For custom actions (READ: settings model `ShortcutAction`s, like `newTab`),
|
||||
we'll hang on to the JSON. When the trigger is hit by the control, it'll raise
|
||||
an event. The app will receive the event, match it up to the trigger, and call
|
||||
`Model.Trigger.Parse(String[] matches) -> ActionAndArgs`. That will do the
|
||||
string replace in the JSON itself (similar to how we do iterable `Command`
|
||||
expansion). We'll then just take the _whole json_, and try to parse it literally
|
||||
as an `ActionAndArgs`. This does mean that we couldn't use `match` as a property
|
||||
for any other future actions.
|
||||
|
||||
We can reuse some of the macros in `ActionArgs.h` to make
|
||||
deserializing to a `Control.Trigger` easy.
|
||||
|
||||
#### Alternative JSON for consideration
|
||||
|
||||
This one encapsulates the actions into the `command` property of the
|
||||
`Terminal.Settings.Model.Trigger`. In this version, we don't need to try and
|
||||
re-parse the entire JSON object as a `ActionAndArgs`. This will let future
|
||||
actions use the `match` property.
|
||||
|
||||
```jsonc
|
||||
"triggers":[
|
||||
{
|
||||
"match": "[Ee][Rr][Rr][Oo][Rr]",
|
||||
"command": {
|
||||
"action": "addMark",
|
||||
"color": "#ff0000"
|
||||
},
|
||||
},
|
||||
{
|
||||
"match": "Terminate batch job \\(Y/N\\)",
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "y\r\n"
|
||||
},
|
||||
},
|
||||
{
|
||||
"match": {
|
||||
"anchor": "bottom",
|
||||
"offset": 4,
|
||||
"length": 5,
|
||||
"pattern": " git push --set-upstream origin ([^\\w]*)",
|
||||
"runOn": "mark"
|
||||
},
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "git push --set-upstream origin ${match[1]}\r"
|
||||
},
|
||||
},
|
||||
{
|
||||
"match": "'(.*)' is not recognized as an internal or external command,",
|
||||
"command": {
|
||||
"action": "checkWinGet",
|
||||
"commandline": "${match[1]}"
|
||||
},
|
||||
},
|
||||
{
|
||||
"match": "This will open a new Terminal tab",
|
||||
"command": "newTab"
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
### Turn text into clickable links
|
||||
|
||||
A similar request from [#8849] that should also be captured. People want the
|
||||
ability to configure the regexes that are used for turning text into clickable
|
||||
links. Currently, we only match on a predefined set<sup>[[1](#footnote-1)]</sup>
|
||||
into clickable text.
|
||||
|
||||
```jsonc
|
||||
// I did not test these regexes
|
||||
{
|
||||
"match": "(^(.+)\\/([^\\/]+)$):(\\d):(\\d)",
|
||||
"action": "clickableLink",
|
||||
"target": "code.exe --goto \"${match[1]}:${match[2]}\""
|
||||
},
|
||||
{
|
||||
"match": "git push --set-upstream origin ([^\\w]*)",
|
||||
"action": "clickableLink",
|
||||
"target": "vi \"${match[1]}\" +${match[2]}\""
|
||||
},
|
||||
{
|
||||
"match": "\\b(T\\d+)\\b",
|
||||
"action": "clickableLink",
|
||||
"target": "https://example.com/tasks/${match[1]}"
|
||||
},
|
||||
```
|
||||
|
||||
Challenges:
|
||||
* with hyperlink matching, we always assume that the cells themselves contain
|
||||
the payload to turn into a URL. We'd have to instead find that the text
|
||||
matched a trigger, then run it back through the regex to split into matched
|
||||
parts, then parse what the target is.
|
||||
|
||||
#### Clickable `sendInput`
|
||||
|
||||
This is the same idea as above. We want users to be able to magically turn
|
||||
regions of the buffer into interactive content. When clicking on that content,
|
||||
the text will get written to the input, rather than `ShellExecute`d.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"match": " git push --set-upstream origin ([^\\w]*)",
|
||||
"action": "clickableSendInput",
|
||||
"input": "git push --set-upstream origin ${match[1]}"
|
||||
},
|
||||
````
|
||||
|
||||
### Layering & Disabling triggers
|
||||
|
||||
Three main cases:
|
||||
* If we define a trigger in `defaults.json`, users should be able to disable it.
|
||||
* If a user puts a trigger into `profiles.defaults`, but doesn't want it to show
|
||||
up in a specific profile
|
||||
* If a fragment decides to add a trigger to a specific profile
|
||||
|
||||
We actually _can't_ put triggers into `defaults.json`. For complicated reasons,
|
||||
we can't have a `profiles.defaults` block in `defaults.json`. This has caused
|
||||
issues in the past. So there's no way for the Terminal to pre-define triggers
|
||||
for all profiles in `defaults.json`. We could entertain the idea of a
|
||||
`defaults.json`-only global `triggers` property, that would work as the
|
||||
"default" triggers. I don't want to commit to that until we have more use cases
|
||||
for default triggers than just the URL detection one.
|
||||
|
||||
We'll just use `"id"` as a key in a map of triggers. Users can unbind them with
|
||||
`"id": "Terminal.builtInThing", match:""`. Just like global actions. So a user can have:
|
||||
|
||||
```json
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
|
||||
"triggers": [
|
||||
{
|
||||
"id": "my winget thing",
|
||||
"match": "'(.*)' is not recognized as an internal or external command,",
|
||||
"command": {
|
||||
"action": "checkWinGet",
|
||||
"commandline": "${match[1]}"
|
||||
},
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"name": "my profile",
|
||||
"commandline": "wsl.exe",
|
||||
"triggers": [
|
||||
{ "id": "my winget thing", "match":"" }
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Triggers do not support partial layering. In the previous example, doing something like:
|
||||
|
||||
```json
|
||||
{ "id": "my winget thing", "command": "newTab" }
|
||||
```
|
||||
|
||||
would not have created a trigger with the same regex match to perform a `newTab`
|
||||
action instead. That json in the profile would be treated as not having a
|
||||
`match`, and would then "unbind" the action from the defaults.
|
||||
|
||||
### Triggers for Terminal Control consumers
|
||||
|
||||
Case in point: [#6969]. Visual Studio would like to supply their own trigger,
|
||||
and receive an event with the ID of the trigger.
|
||||
|
||||
> * Consumers of the terminal must be able to provide a pattern that will identify regions of clickable text
|
||||
> * Consumers will provide a callback that is called with the clicked text as a parameter
|
||||
|
||||
These are addressed generally by the rest of the spec. They could provide
|
||||
`Control.TriggerAndMatch` via `ICoreSettings`. They can use the built-in control
|
||||
actions, or similar to the Windows Terminal, handle custom actions on the
|
||||
control's `CustomAction` event handler.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
<table>
|
||||
|
||||
<tr><td><strong>Compatibility</strong></td><td>
|
||||
|
||||
How does someone turn off the built-in hyperlink detector? Currently, the
|
||||
Terminal has a global setting `experimental.detectURLs` to enable auto-detection
|
||||
of URLs. That should theoretically be replaced with an equivalent setting:
|
||||
|
||||
```json
|
||||
"triggers": [
|
||||
{
|
||||
"match": "(\b(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|$!:,.;]*[A-Za-z0-9+&@#/%=~_|$])",
|
||||
"action": "clickableLink",
|
||||
"target": "https://example.com/tasks/${match[1]}"
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
However, as mentioned before, this _can't_ go in
|
||||
`defaults.json@profiles.defaults`. We could:
|
||||
|
||||
* Promote the global `experimental.detectURLs` to a fully fledged setting
|
||||
`detectURLs`. This would automatically insert this trigger into the list of
|
||||
triggers whenever it is enabled. We could make it a per-profile setting. We'll
|
||||
just always add it at the end of their list of triggers when creating settings
|
||||
for the profile.
|
||||
* Alternatively, implicitly have a "hardcoded default" trigger with the id of
|
||||
`"Microsoft.Terminal.DetectUrls"`. That would let folks opt-out with
|
||||
```json
|
||||
{ "id": "Microsoft.Terminal.DetectUrls", "match":"" }
|
||||
```
|
||||
|
||||
|
||||
</td></tr>
|
||||
|
||||
<tr><td><strong>Accessibility</strong></td><td>
|
||||
|
||||
Clickable links and sendInput will get the same appearance treatment as
|
||||
auto-detected links (but not as manually emitted ones). They'll have the same
|
||||
accessibility properties as clickable text does today.
|
||||
|
||||
Otherwise, no accessibility changes expected.
|
||||
|
||||
</td></tr>
|
||||
|
||||
<tr><td><strong>Sustainability</strong></td><td>
|
||||
|
||||
No sustainability changes expected.
|
||||
|
||||
</td></tr>
|
||||
|
||||
<tr><td><strong>Localization</strong></td><td>
|
||||
|
||||
No localization concerns here.
|
||||
|
||||
</td></tr>
|
||||
|
||||
</table>
|
||||
|
||||
* Q: What happens if multiple regexes match the same text
|
||||
- A: We'll apply the regexes in the order they're defined in the JSON.
|
||||
|
||||
|
||||
### Future Considerations
|
||||
|
||||
* The "iTerm2 trigger actions" listed below are a great list of potential
|
||||
follow-up ways these could be used.
|
||||
|
||||
<!-- This was originally in this doc, but I think that should be moved to it's own spec -->
|
||||
<!--
|
||||
#### Clickable sendInput
|
||||
|
||||
There's already an existing standard for "this text is a hyperlink" - [OSC8].
|
||||
That already accepts a list of arbitrary parameters. Without even really
|
||||
_extending_ that sequence, we could accept a `input={some string of input}`
|
||||
parameter. When we see a URL that was output by a client app with that
|
||||
parameter, we could make clicking that text write that input to the terminal, as
|
||||
input, rather than using the text as a URL.
|
||||
|
||||
We wouldn't need to worry about people sneaking tabs, enters, backspaces into
|
||||
the input via this - control characters aren't accepted as part of these params.
|
||||
We would need to figure out how `:` and `;` should be escaped. Those are
|
||||
delimiters of the sequence itself, so they would need to be escaped / translated
|
||||
somehow.
|
||||
|
||||
We'd need to be wary of things like some of the extended unicode characters - we
|
||||
don't really want folks clicking links and having commands that do something
|
||||
like `echo foo ; rm -rf`.
|
||||
|
||||
This seems like something that could get roped in with triggers. The [iTerm2
|
||||
docs] have a *Make Hyperlink* action, which can be used to turn regex-matched
|
||||
text into a hyperlink. We could always author our equivalent to allow for *Make
|
||||
Clickable* with a string of input instead. -->
|
||||
|
||||
## Resources
|
||||
|
||||
### iTerm2 trigger actions
|
||||
|
||||
The following list was taken from the [iTerm2 docs]. I've added notes on ways we
|
||||
could implement similar functionality. Where there's room for us to easily add
|
||||
new Control-level actions, I've added the enum name as a sub-point.
|
||||
|
||||
* *Annotate*: Add a bookmark? ✅
|
||||
* *Bounce Dock Icon*: Visual, window bell? We don't have a manual action for something like this
|
||||
* `Control.TriggerType.RaiseBell`
|
||||
* *Capture Output*: Not really a good analog. Maybe "add a bookmark", with a bookmark list pane.
|
||||
* *Highlight Text*: `experimental.colorSelection` above ✅
|
||||
* *Inject Data*: "Injects a string as though it had been received". This isn't `sendInput`, it's like `sendOutput`. Dangerous for the same reasons that `experimental.colorSelection` is - modifications only to the terminal-side of the conpty buffer.
|
||||
* *Invoke Script Function*: We have nothing like this. I suppose the general-purpose actions are vaguely like this?
|
||||
* *Make Hyperlink*: `clickableLink` above ✅
|
||||
* *Open Password Manager*: Nothing similar.
|
||||
* *Post Notification*: Consider [#7718] - could absolutely be done.
|
||||
* `Control.TriggerType.SendNotification`
|
||||
* *Prompt Detected*: `addMark(prompt)`, basically. ✅
|
||||
* *Report Directory*: "Tells iTerm2 what your current directory is". Kinda like `sendOutput`, but not that dangerous in my opinion.
|
||||
* `Control.TriggerType.SetWorkingDirectory`
|
||||
* *Report User & Host*: Kinda the same as the above. We don't use these currently, but we may want to consider in the future.
|
||||
* *Ring Bell*: Plays the standard system bell sound once.
|
||||
* `Control.TriggerType.RaiseBell`
|
||||
* *Run Command*: This seems dangerous if misused. Basically just `ShellExecute()` the command.
|
||||
* *Run Co-process*: Definitely no precedent we have for this, and might require its own spec.
|
||||
* *Run Silent Co-process*: same deal.
|
||||
* *Send Text*: `sendInput` ✅
|
||||
* *Set Mark*: `addMark` ✅
|
||||
* *Set Title*: Similar to the "Report Directory" above.
|
||||
* `Control.TriggerType.SetTitle`
|
||||
* *Set User Variable*: Specifically tied to scripting, which we don't have.
|
||||
* *Show Alert*: Show a toast? [#8592]
|
||||
* `Control.TriggerType.SendInAppToast`
|
||||
* *Stop Processing Triggers*: Definitely an interesting idea. Stop processing more triggers.
|
||||
|
||||
Almost all these could be control-level actions that don't _need_ to cross out
|
||||
of the ControlCore (outside of existing conceived notions for crossing the
|
||||
boundary, like a visual bell or a notification).
|
||||
|
||||
|
||||
### Footnotes
|
||||
|
||||
<a name="footnote-1"><a>[1]: The regex we currently use for URLs is `(\b(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|$!:,.;]*[A-Za-z0-9+&@#/%=~_|$])`.
|
||||
|
||||
|
||||
[#5916]: https://github.com/microsoft/terminal/issues/5916
|
||||
[#8849]: https://github.com/microsoft/terminal/issues/8849
|
||||
[#7718]: https://github.com/microsoft/terminal/issues/7718
|
||||
[#8592]: https://github.com/microsoft/terminal/issues/8592
|
||||
[#7562]: https://github.com/microsoft/terminal/issues/7562
|
||||
[#8294]: https://github.com/microsoft/terminal/issues/8294
|
||||
[#2671]: https://github.com/microsoft/terminal/issues/2671
|
||||
[#6969]: https://github.com/microsoft/terminal/issues/6969
|
||||
[#11901]: https://github.com/microsoft/terminal/issues/11901
|
||||
[#11000]: https://github.com/microsoft/terminal/issues/11000
|
||||
|
||||
[OSC8]: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
|
||||
[triggers in iTerm2]: https://iterm2.com/documentation-one-page.html#documentation-triggers.html
|
||||
[iTerm2 docs]: https://iterm2.com/documentation-one-page.html#documentation-triggers.html
|
||||
[VsCode "auto replies"]: https://code.visualstudio.com/docs/terminal/advanced#_auto-replies
|
||||
[Quick Fix API]: https://github.com/microsoft/vscode/blob/4c6e0b5eee419550e31e386a0a5ca9840ac3280b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts#L80-L146
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
@@ -1,90 +0,0 @@
|
||||
---
|
||||
author: Dustin L. Howett @DHowett
|
||||
created on: 2023-03-22
|
||||
last updated: 2023-03-22
|
||||
issue id: none
|
||||
---
|
||||
|
||||
# Windows Terminal "Portable" Mode
|
||||
|
||||
## Abstract
|
||||
|
||||
Since we are planning on officially supporting unpackaged execution, I propose a special mode where Terminal stores its
|
||||
settings in a `settings` folder next to `WindowsTerminal.exe`.
|
||||
|
||||
## Inspiration
|
||||
|
||||
- [PortableApps](https://portableapps.com)
|
||||
- "Embeddable" Python, which relies on the deployment of a specific file to the Python root
|
||||
|
||||
## Solution Design
|
||||
|
||||
- _If running without package identity,_ `CascadiaSettings` will look for the presence of a file called `.portable` next
|
||||
to `Microsoft.Terminal.Settings.Model.dll`.
|
||||
- If that file is present, it will change the settings and state paths to be rooted in a subfolder named `settings` next
|
||||
to `Microsoft.Terminal.Settings.Model.dll`.
|
||||
|
||||
Right now, _the only thing_ that makes Terminal not work in a "portable" manner is that it saves settings to
|
||||
`%LOCALAPPDATA%`.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
_No UI/UX impact is expected._
|
||||
|
||||
## Capabilities
|
||||
|
||||
- Distributors could ship a self-contained and preconfigured Terminal installation.
|
||||
- Users could archive fully-working preconfigured versions of Terminal.
|
||||
- Developers (such as those on the team) could easily test multiple versions of Terminal without worrying about global
|
||||
settings pollution.
|
||||
|
||||
### Accessibility
|
||||
|
||||
_No change is expected._
|
||||
|
||||
### Security
|
||||
|
||||
_No change is expected._
|
||||
|
||||
### Reliability
|
||||
|
||||
More code always bears a risk.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This is a net new feature, and it does not break any existing features. A distributor (or a user) can opt in (or out) by
|
||||
adding (or removing) the `.portable` file.
|
||||
|
||||
The following features may be impacted.
|
||||
|
||||
- **Dynamic Profiles** and **Fragment Extensions**
|
||||
- _No impact expected._ Dynamic profiles will still be generated. If a portable installation is moved to a machine without the dynamic profile source, that profile will disappear.
|
||||
- `firstWindowPreference` and `state.json`
|
||||
- _No impact expected._
|
||||
- State is stored next to settings, even for portable installations.
|
||||
- If a dynamic profile was saved in `state` and has been removed, Terminal will proceed as in non-portable mode.
|
||||
- Moving an install from Windows 10 to Windows 11 and back
|
||||
- _No impact expected._
|
||||
- "Machine-specific" settings, like those about rendering and repainting
|
||||
- _No impact expected._
|
||||
- Terminal does not distinguish settings that are specific to a machine. These settings will move along with the portable install.
|
||||
- The shell extension
|
||||
- _No impact expected._
|
||||
- The shell extension will not be registered with Windows.
|
||||
- If we choose to register the shell extension, it is already prepared for running a version of WT from the same directory. Registering the portable shell extension will make it launch portable Terminal.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
_No change is expected._
|
||||
|
||||
## Potential Issues
|
||||
|
||||
- User confusion around where settings are stored.
|
||||
|
||||
## Future considerations
|
||||
|
||||
- In the future, perhaps `.portable` could itself contain a directory path into which we would store settings.
|
||||
- We could consider adding an indicator in the Settings UI.
|
||||
- Because we are using the module path of the Settings Model DLL, a future unpackaged version of the shell extension
|
||||
that supports profile loading would read the right settings file (assuming it used the settings model.)
|
||||
- If we choose to store the shell extension cache in the registry, we would need to avoid doing so in portable mode.
|
||||
@@ -1,37 +0,0 @@
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
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.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
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 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.
|
||||
@@ -1,4 +0,0 @@
|
||||
### Notes for Future Maintainers
|
||||
|
||||
Search for files prefixed with `stb_` in this project.
|
||||
At the time of writing, the only file being used is `stb_rect_pack.h`.
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/component-detection-manifest.json",
|
||||
"Registrations": [
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/nothings/stb",
|
||||
"commitHash": "5736b15f7ea0ffb08dd38af21067c314d6a3aae9"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
@@ -1,623 +0,0 @@
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
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.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
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 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.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -6,14 +6,14 @@ SamplerState samplerState;
|
||||
|
||||
// Terminal settings such as the resolution of the texture
|
||||
cbuffer PixelShaderSettings {
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
// Resolution of the shaderTexture
|
||||
float2 Resolution;
|
||||
// Background color as rgba
|
||||
float4 Background;
|
||||
// The number of seconds since the pixel shader was enabled
|
||||
float Time;
|
||||
// UI Scale
|
||||
float Scale;
|
||||
// Resolution of the shaderTexture
|
||||
float2 Resolution;
|
||||
// Background color as rgba
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
|
||||
@@ -29,19 +29,38 @@ float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
// effect, read the colors offset on the left, right, top, bottom of this
|
||||
// fragment, as well as on the corners of this fragment.
|
||||
//
|
||||
// You could get away with fewer samples, but the resulting outlines will be
|
||||
// blurrier.
|
||||
|
||||
//left, right, top, bottom:
|
||||
float4 leftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 0.0)/Resolution.y);
|
||||
float4 rightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 0.0)/Resolution.y);
|
||||
float4 topColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, 1.0)/Resolution.y);
|
||||
float4 bottomColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, -1.0)/Resolution.y);
|
||||
|
||||
// Corners
|
||||
float4 topLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 1.0)/Resolution.y);
|
||||
float4 topRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 1.0)/Resolution.y);
|
||||
float4 bottomLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, -1.0)/Resolution.y);
|
||||
float4 bottomRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, -1.0)/Resolution.y);
|
||||
|
||||
|
||||
// Now, if any of those adjacent cells has text in it, then the *color vec4
|
||||
// will have a non-zero .w (which is used for alpha). Use that alpha value
|
||||
// to add some black to the current fragment.
|
||||
//
|
||||
// This will result in only coloring fragments adjacent to text, but leaving
|
||||
// background images (for example) untouched.
|
||||
float3 outlineColor = float3(0, 0, 0);
|
||||
float4 result = color;
|
||||
result = result + float4(outlineColor, leftColor.w);
|
||||
result = result + float4(outlineColor, rightColor.w);
|
||||
result = result + float4(outlineColor, topColor.w);
|
||||
result = result + float4(outlineColor, bottomColor.w);
|
||||
|
||||
for (int dy = -2; dy <= 2; dy += 2) {
|
||||
for (int dx = -2; dx <= 2; dx += 2) {
|
||||
float4 neighbor = shaderTexture.Sample(samplerState, tex, int2(dx, dy));
|
||||
color.a += neighbor.a;
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
result = result + float4(outlineColor, topLeftColor.w);
|
||||
result = result + float4(outlineColor, topRightColor.w);
|
||||
result = result + float4(outlineColor, bottomLeftColor.w);
|
||||
result = result + float4(outlineColor, bottomRightColor.w);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<packages>
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
<TerminalVCRTForwarders>true</TerminalVCRTForwarders>
|
||||
<TerminalThemeHelpers>true</TerminalThemeHelpers>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -195,6 +197,26 @@
|
||||
<!-- **END VC LIBS HACK** -->
|
||||
|
||||
|
||||
<!-- **BEGIN TERMINAL CONNECTION HACK** -->
|
||||
<!-- This is the same as the above VC libs hack, but for TerminalConnection.
|
||||
TerminalConnection depends on cpprest*.dll, and if we don't include it in
|
||||
the packaging output, we'll crash as soon as we try to load
|
||||
TerminalConnection.dll.
|
||||
|
||||
The Sample sln needs to do this manually - the real exe has a
|
||||
ProjectReference to TerminalConnection.vcxproj and can figure this out on
|
||||
its own. -->
|
||||
<ItemGroup>
|
||||
<_TerminalConnectionDlls Include="$(OpenConsoleCommonOutDir)\TerminalConnection\*.dll" />
|
||||
|
||||
<PackagingOutputs Include="@(_TerminalConnectionDlls)">
|
||||
<ProjectName>$(ProjectName)</ProjectName>
|
||||
<OutputGroup>BuiltProjectOutputGroup</OutputGroup>
|
||||
<TargetPath>%(Filename)%(Extension)</TargetPath>
|
||||
</PackagingOutputs>
|
||||
</ItemGroup>
|
||||
<!-- **END TERMINAL CONNECTION HACK** -->
|
||||
|
||||
<!-- Same thing again here, with WindowsTerminal.exe -->
|
||||
<ItemGroup>
|
||||
<_WindowsTerminalExe Include="$(OpenConsoleCommonOutDir)\WindowsTerminal\*.exe" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.230207.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -113,7 +113,7 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const
|
||||
// - This is an iterator over legacy colors only. The text is not modified.
|
||||
// Arguments:
|
||||
// - legacyAttrs - One legacy color item per cell
|
||||
OutputCellIterator::OutputCellIterator(const std::span<const WORD> legacyAttrs) noexcept :
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const WORD> legacyAttrs) noexcept :
|
||||
_mode(Mode::LegacyAttr),
|
||||
_currentView(s_GenerateViewLegacyAttr(til::at(legacyAttrs, 0))),
|
||||
_run(legacyAttrs),
|
||||
@@ -128,7 +128,7 @@ OutputCellIterator::OutputCellIterator(const std::span<const WORD> legacyAttrs)
|
||||
// - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute.
|
||||
// Arguments:
|
||||
// - charInfos - Multiple cell with unicode text and legacy color data.
|
||||
OutputCellIterator::OutputCellIterator(const std::span<const CHAR_INFO> charInfos) noexcept :
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept :
|
||||
_mode(Mode::CharInfo),
|
||||
_currentView(s_GenerateView(til::at(charInfos, 0))),
|
||||
_run(charInfos),
|
||||
@@ -143,7 +143,7 @@ OutputCellIterator::OutputCellIterator(const std::span<const CHAR_INFO> charInfo
|
||||
// - This is an iterator over existing OutputCells with full text and color data.
|
||||
// Arguments:
|
||||
// - cells - Multiple cells in a run
|
||||
OutputCellIterator::OutputCellIterator(const std::span<const OutputCell> cells) :
|
||||
OutputCellIterator::OutputCellIterator(const gsl::span<const OutputCell> cells) :
|
||||
_mode(Mode::Cell),
|
||||
_currentView(s_GenerateView(til::at(cells, 0))),
|
||||
_run(cells),
|
||||
@@ -181,15 +181,15 @@ OutputCellIterator::operator bool() const noexcept
|
||||
}
|
||||
case Mode::Cell:
|
||||
{
|
||||
return _pos < std::get<std::span<const OutputCell>>(_run).size();
|
||||
return _pos < std::get<gsl::span<const OutputCell>>(_run).size();
|
||||
}
|
||||
case Mode::CharInfo:
|
||||
{
|
||||
return _pos < std::get<std::span<const CHAR_INFO>>(_run).size();
|
||||
return _pos < std::get<gsl::span<const CHAR_INFO>>(_run).size();
|
||||
}
|
||||
case Mode::LegacyAttr:
|
||||
{
|
||||
return _pos < std::get<std::span<const WORD>>(_run).size();
|
||||
return _pos < std::get<gsl::span<const WORD>>(_run).size();
|
||||
}
|
||||
default:
|
||||
FAIL_FAST_HR(E_NOTIMPL);
|
||||
@@ -198,11 +198,6 @@ OutputCellIterator::operator bool() const noexcept
|
||||
CATCH_FAIL_FAST();
|
||||
}
|
||||
|
||||
size_t OutputCellIterator::Position() const noexcept
|
||||
{
|
||||
return _pos;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Advances the iterator one position over the underlying data source.
|
||||
// Return Value:
|
||||
@@ -268,7 +263,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateView(til::at(std::get<std::span<const OutputCell>>(_run), _pos));
|
||||
_currentView = s_GenerateView(til::at(std::get<gsl::span<const OutputCell>>(_run), _pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -278,7 +273,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateView(til::at(std::get<std::span<const CHAR_INFO>>(_run), _pos));
|
||||
_currentView = s_GenerateView(til::at(std::get<gsl::span<const CHAR_INFO>>(_run), _pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -288,7 +283,7 @@ OutputCellIterator& OutputCellIterator::operator++()
|
||||
_pos++;
|
||||
if (operator bool())
|
||||
{
|
||||
_currentView = s_GenerateViewLegacyAttr(til::at(std::get<std::span<const WORD>>(_run), _pos));
|
||||
_currentView = s_GenerateViewLegacyAttr(til::at(std::get<gsl::span<const WORD>>(_run), _pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -39,16 +39,15 @@ public:
|
||||
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const std::wstring_view utf16Text) noexcept;
|
||||
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute& attribute, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const std::span<const WORD> legacyAttributes) noexcept;
|
||||
OutputCellIterator(const std::span<const CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const std::span<const OutputCell> cells);
|
||||
OutputCellIterator(const gsl::span<const WORD> legacyAttributes) noexcept;
|
||||
OutputCellIterator(const gsl::span<const CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const gsl::span<const OutputCell> cells);
|
||||
~OutputCellIterator() = default;
|
||||
|
||||
OutputCellIterator& operator=(const OutputCellIterator& it) = default;
|
||||
|
||||
operator bool() const noexcept;
|
||||
|
||||
size_t Position() const noexcept;
|
||||
til::CoordType GetCellDistance(OutputCellIterator other) const noexcept;
|
||||
til::CoordType GetInputDistance(OutputCellIterator other) const noexcept;
|
||||
friend til::CoordType operator-(OutputCellIterator one, OutputCellIterator two) = delete;
|
||||
@@ -87,13 +86,13 @@ private:
|
||||
};
|
||||
Mode _mode;
|
||||
|
||||
std::span<const WORD> _legacyAttrs;
|
||||
gsl::span<const WORD> _legacyAttrs;
|
||||
|
||||
std::variant<
|
||||
std::wstring_view,
|
||||
std::span<const WORD>,
|
||||
std::span<const CHAR_INFO>,
|
||||
std::span<const OutputCell>,
|
||||
gsl::span<const WORD>,
|
||||
gsl::span<const CHAR_INFO>,
|
||||
gsl::span<const OutputCell>,
|
||||
std::monostate>
|
||||
_run;
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ OutputCellRect::OutputCellRect(const til::CoordType rows, const til::CoordType c
|
||||
// - row - The Y position or row index in the buffer.
|
||||
// Return Value:
|
||||
// - Read/write span of OutputCells
|
||||
std::span<OutputCell> OutputCellRect::GetRow(const til::CoordType row)
|
||||
gsl::span<OutputCell> OutputCellRect::GetRow(const til::CoordType row)
|
||||
{
|
||||
return std::span<OutputCell>(_FindRowOffset(row), _cols);
|
||||
return gsl::span<OutputCell>(_FindRowOffset(row), _cols);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -47,7 +47,7 @@ std::span<OutputCell> OutputCellRect::GetRow(const til::CoordType row)
|
||||
// - Read-only iterator of OutputCells
|
||||
OutputCellIterator OutputCellRect::GetRowIter(const til::CoordType row) const
|
||||
{
|
||||
const std::span<const OutputCell> view(_FindRowOffset(row), _cols);
|
||||
const gsl::span<const OutputCell> view(_FindRowOffset(row), _cols);
|
||||
|
||||
return OutputCellIterator(view);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
OutputCellRect() noexcept;
|
||||
OutputCellRect(const til::CoordType rows, const til::CoordType cols);
|
||||
|
||||
std::span<OutputCell> GetRow(const til::CoordType row);
|
||||
gsl::span<OutputCell> GetRow(const til::CoordType row);
|
||||
OutputCellIterator GetRowIter(const til::CoordType row) const;
|
||||
|
||||
til::CoordType Height() const noexcept;
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
#include "precomp.h"
|
||||
#include "Row.hpp"
|
||||
|
||||
#include <til/unicode.h>
|
||||
|
||||
#include "textBuffer.hpp"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
|
||||
extern "C" int __isa_available;
|
||||
|
||||
// The STL is missing a std::iota_n analogue for std::iota, so I made my own.
|
||||
template<typename OutIt, typename Diff, typename T>
|
||||
@@ -84,7 +79,23 @@ ROW::ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, c
|
||||
_attr{ rowWidth, fillAttribute },
|
||||
_columnCount{ rowWidth }
|
||||
{
|
||||
_init();
|
||||
if (_chars.data())
|
||||
{
|
||||
_init();
|
||||
}
|
||||
}
|
||||
|
||||
void swap(ROW& lhs, ROW& rhs) noexcept
|
||||
{
|
||||
std::swap(lhs._charsBuffer, rhs._charsBuffer);
|
||||
std::swap(lhs._charsHeap, rhs._charsHeap);
|
||||
std::swap(lhs._chars, rhs._chars);
|
||||
std::swap(lhs._charOffsets, rhs._charOffsets);
|
||||
std::swap(lhs._attr, rhs._attr);
|
||||
std::swap(lhs._columnCount, rhs._columnCount);
|
||||
std::swap(lhs._lineRendition, rhs._lineRendition);
|
||||
std::swap(lhs._wrapForced, rhs._wrapForced);
|
||||
std::swap(lhs._doubleBytePadded, rhs._doubleBytePadded);
|
||||
}
|
||||
|
||||
void ROW::SetWrapForced(const bool wrap) noexcept
|
||||
@@ -117,25 +128,17 @@ LineRendition ROW::GetLineRendition() const noexcept
|
||||
return _lineRendition;
|
||||
}
|
||||
|
||||
uint16_t ROW::GetLineWidth() const noexcept
|
||||
{
|
||||
const auto scale = _lineRendition != LineRendition::SingleWidth ? 1 : 0;
|
||||
return _columnCount >> scale;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets all properties of the ROW to default values
|
||||
// Arguments:
|
||||
// - Attr - The default attribute (color) to fill
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ROW::Reset(const TextAttribute& attr) noexcept
|
||||
void ROW::Reset(const TextAttribute& attr)
|
||||
{
|
||||
_charsHeap.reset();
|
||||
_chars = { _charsBuffer, _columnCount };
|
||||
// Constructing and then moving objects into place isn't free.
|
||||
// Modifying the existing object is _much_ faster.
|
||||
*_attr.runs().unsafe_shrink_to_size(1) = til::rle_pair{ attr, _columnCount };
|
||||
_attr = { _columnCount, attr };
|
||||
_lineRendition = LineRendition::SingleWidth;
|
||||
_wrapForced = false;
|
||||
_doubleBytePadded = false;
|
||||
@@ -144,117 +147,80 @@ void ROW::Reset(const TextAttribute& attr) noexcept
|
||||
|
||||
void ROW::_init() noexcept
|
||||
{
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26462) // The value pointed to by '...' is assigned only once, mark it as a pointer to const (con.4).
|
||||
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
#pragma warning(disable : 26490) // Don't use reinterpret_cast (type.1).
|
||||
std::fill_n(_chars.begin(), _columnCount, UNICODE_SPACE);
|
||||
std::iota(_charOffsets.begin(), _charOffsets.end(), uint16_t{ 0 });
|
||||
}
|
||||
|
||||
// Fills _charsBuffer with whitespace and correspondingly _charOffsets
|
||||
// with successive numbers from 0 to _columnCount+1.
|
||||
#if defined(TIL_SSE_INTRINSICS)
|
||||
alignas(__m256i) static constexpr uint16_t whitespaceData[]{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
|
||||
alignas(__m256i) static constexpr uint16_t offsetsData[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
alignas(__m256i) static constexpr uint16_t increment16Data[]{ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 };
|
||||
alignas(__m128i) static constexpr uint16_t increment8Data[]{ 8, 8, 8, 8, 8, 8, 8, 8 };
|
||||
|
||||
// The AVX loop operates on 32 bytes at a minimum. Since _charsBuffer/_charOffsets uses 2 byte large
|
||||
// wchar_t/uint16_t respectively, this translates to 16-element writes, which equals a _columnCount of 15,
|
||||
// because it doesn't include the past-the-end char-offset as described in the _charOffsets member comment.
|
||||
if (__isa_available >= __ISA_AVAILABLE_AVX2 && _columnCount >= 15)
|
||||
// Routine Description:
|
||||
// - resizes ROW to new width
|
||||
// Arguments:
|
||||
// - charsBuffer - a new backing buffer to use for _charsBuffer
|
||||
// - charOffsetsBuffer - a new backing buffer to use for _charOffsets
|
||||
// - rowWidth - the new width, in cells
|
||||
// - fillAttribute - the attribute to use for any newly added, trailing cells
|
||||
void ROW::Resize(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute)
|
||||
{
|
||||
// A default-constructed ROW has no cols/chars to copy.
|
||||
// It can be detected by the lack of a _charsBuffer (among others).
|
||||
//
|
||||
// Otherwise, this block figures out how much we can copy into the new `rowWidth`.
|
||||
uint16_t colsToCopy = 0;
|
||||
uint16_t charsToCopy = 0;
|
||||
if (_charsBuffer)
|
||||
{
|
||||
auto chars = _charsBuffer;
|
||||
auto charOffsets = _charOffsets.data();
|
||||
|
||||
// The backing buffer for both chars and charOffsets is guaranteed to be 16-byte aligned,
|
||||
// but AVX operations are 32-byte large. As such, when we write out the last chunk, we
|
||||
// have to align it to the ends of the 2 buffers. This results in a potential overlap of
|
||||
// 16 bytes between the last write in the main loop below and the final write afterwards.
|
||||
//
|
||||
// An example:
|
||||
// If you have a terminal between 16 and 23 columns the buffer has a size of 48 bytes.
|
||||
// The main loop below will iterate once, as it writes out bytes 0-31 and then exits.
|
||||
// The final write afterwards cannot write bytes 32-63 because that would write
|
||||
// out of bounds. Instead it writes bytes 16-47, overwriting 16 overlapping bytes.
|
||||
// This is better than branching and switching to SSE2, because both things are slow.
|
||||
//
|
||||
// Since we want to exit the main loop with at least 1 write left to do as the final write,
|
||||
// we need to subtract 1 alignment from the buffer length (= 16 bytes). Since _columnCount is
|
||||
// in wchar_t's we subtract -8. The same applies to the ~7 here vs ~15. If you squint slightly
|
||||
// you'll see how this is effectively the inverse of what CalculateCharsBufferStride does.
|
||||
const auto tailColumnOffset = gsl::narrow_cast<uint16_t>((_columnCount - 8u) & ~7);
|
||||
const auto charsEndLoop = chars + tailColumnOffset;
|
||||
const auto charOffsetsEndLoop = charOffsets + tailColumnOffset;
|
||||
|
||||
const auto whitespace = _mm256_load_si256(reinterpret_cast<const __m256i*>(&whitespaceData[0]));
|
||||
auto offsetsLoop = _mm256_load_si256(reinterpret_cast<const __m256i*>(&offsetsData[0]));
|
||||
const auto offsets = _mm256_add_epi16(offsetsLoop, _mm256_set1_epi16(tailColumnOffset));
|
||||
|
||||
if (chars < charsEndLoop)
|
||||
colsToCopy = std::min(rowWidth, _columnCount);
|
||||
// Safety: colsToCopy is [0, _columnCount].
|
||||
charsToCopy = _uncheckedCharOffset(colsToCopy);
|
||||
// Safety: colsToCopy is [0, _columnCount] due to colsToCopy != 0.
|
||||
for (; colsToCopy != 0 && _uncheckedIsTrailer(colsToCopy); --colsToCopy)
|
||||
{
|
||||
const auto increment = _mm256_load_si256(reinterpret_cast<const __m256i*>(&increment16Data[0]));
|
||||
|
||||
do
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(chars), whitespace);
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(charOffsets), offsetsLoop);
|
||||
offsetsLoop = _mm256_add_epi16(offsetsLoop, increment);
|
||||
chars += 16;
|
||||
charOffsets += 16;
|
||||
} while (chars < charsEndLoop);
|
||||
}
|
||||
}
|
||||
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(charsEndLoop), whitespace);
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(charOffsetsEndLoop), offsets);
|
||||
// If we grow the row width, we have to append a bunch of whitespace.
|
||||
// `trailingWhitespace` stores that amount.
|
||||
// Safety: The preceding block left colsToCopy in the range [0, rowWidth].
|
||||
const uint16_t trailingWhitespace = rowWidth - colsToCopy;
|
||||
|
||||
// Allocate memory for the new `_chars` array.
|
||||
// Use the provided charsBuffer if possible, otherwise allocate a `_charsHeap`.
|
||||
std::unique_ptr<wchar_t[]> charsHeap;
|
||||
std::span chars{ charsBuffer, rowWidth };
|
||||
const std::span charOffsets{ charOffsetsBuffer, ::base::strict_cast<size_t>(rowWidth) + 1u };
|
||||
if (const uint16_t charsCapacity = charsToCopy + trailingWhitespace; charsCapacity > rowWidth)
|
||||
{
|
||||
charsHeap = std::make_unique_for_overwrite<wchar_t[]>(charsCapacity);
|
||||
chars = { charsHeap.get(), charsCapacity };
|
||||
}
|
||||
|
||||
// Copy chars and charOffsets over.
|
||||
{
|
||||
const auto it = std::copy_n(_chars.begin(), charsToCopy, chars.begin());
|
||||
std::fill_n(it, trailingWhitespace, L' ');
|
||||
}
|
||||
{
|
||||
const auto it = std::copy_n(_charOffsets.begin(), colsToCopy, charOffsets.begin());
|
||||
// The _charOffsets array is 1 wider than newWidth indicates.
|
||||
// This is because the extra column contains the past-the-end index into _chars.
|
||||
iota_n(it, trailingWhitespace + 1u, charsToCopy);
|
||||
}
|
||||
|
||||
_charsBuffer = charsBuffer;
|
||||
_charsHeap = std::move(charsHeap);
|
||||
_chars = chars;
|
||||
_charOffsets = charOffsets;
|
||||
_columnCount = rowWidth;
|
||||
|
||||
// .resize_trailing_extent() doesn't work if the vector is empty,
|
||||
// since there's no trailing item that could be extended.
|
||||
if (_attr.empty())
|
||||
{
|
||||
_attr = { rowWidth, fillAttribute };
|
||||
}
|
||||
else
|
||||
{
|
||||
auto chars = _charsBuffer;
|
||||
auto charOffsets = _charOffsets.data();
|
||||
const auto charsEnd = chars + _columnCount;
|
||||
|
||||
const auto whitespace = _mm_load_si128(reinterpret_cast<const __m128i*>(&whitespaceData[0]));
|
||||
const auto increment = _mm_load_si128(reinterpret_cast<const __m128i*>(&increment8Data[0]));
|
||||
auto offsets = _mm_load_si128(reinterpret_cast<const __m128i*>(&offsetsData[0]));
|
||||
|
||||
do
|
||||
{
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(chars), whitespace);
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(charOffsets), offsets);
|
||||
offsets = _mm_add_epi16(offsets, increment);
|
||||
chars += 8;
|
||||
charOffsets += 8;
|
||||
// If _columnCount is something like 120, the actual backing buffer for charOffsets is 121 items large.
|
||||
// --> The while loop uses <= to emit at least 1 more write.
|
||||
} while (chars <= charsEnd);
|
||||
_attr.resize_trailing_extent(rowWidth);
|
||||
}
|
||||
#elif defined(TIL_ARM_NEON_INTRINSICS)
|
||||
alignas(uint16x8_t) static constexpr uint16_t offsetsData[]{ 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
auto chars = _charsBuffer;
|
||||
auto charOffsets = _charOffsets.data();
|
||||
const auto charsEnd = chars + _columnCount;
|
||||
|
||||
const auto whitespace = vdupq_n_u16(L' ');
|
||||
const auto increment = vdupq_n_u16(8);
|
||||
auto offsets = vld1q_u16(&offsetsData[0]);
|
||||
|
||||
do
|
||||
{
|
||||
vst1q_u16(chars, whitespace);
|
||||
vst1q_u16(charOffsets, offsets);
|
||||
offsets = vaddq_u16(offsets, increment);
|
||||
chars += 8;
|
||||
charOffsets += 8;
|
||||
// If _columnCount is something like 120, the actual backing buffer for charOffsets is 121 items large.
|
||||
// --> The while loop uses <= to emit at least 1 more write.
|
||||
} while (chars <= charsEnd);
|
||||
#else
|
||||
#error "Vectorizing this function improves overall performance by up to 40%. Don't remove this warning, just add the vectorized code."
|
||||
std::fill_n(_charsBuffer, _columnCount, UNICODE_SPACE);
|
||||
std::iota(_charOffsets.begin(), _charOffsets.end(), uint16_t{ 0 });
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
}
|
||||
|
||||
void ROW::TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth)
|
||||
@@ -263,49 +229,6 @@ void ROW::TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& a
|
||||
_attr.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
|
||||
}
|
||||
|
||||
void ROW::CopyFrom(const ROW& source)
|
||||
{
|
||||
RowCopyTextFromState state{ .source = source };
|
||||
CopyTextFrom(state);
|
||||
TransferAttributes(source.Attributes(), _columnCount);
|
||||
_lineRendition = source._lineRendition;
|
||||
_wrapForced = source._wrapForced;
|
||||
}
|
||||
|
||||
// Returns the previous possible cursor position, preceding the given column.
|
||||
// Returns 0 if column is less than or equal to 0.
|
||||
til::CoordType ROW::NavigateToPrevious(til::CoordType column) const noexcept
|
||||
{
|
||||
return _adjustBackward(_clampedColumn(column - 1));
|
||||
}
|
||||
|
||||
// Returns the next possible cursor position, following the given column.
|
||||
// Returns the row width if column is beyond the width of the row.
|
||||
til::CoordType ROW::NavigateToNext(til::CoordType column) const noexcept
|
||||
{
|
||||
return _adjustForward(_clampedColumn(column + 1));
|
||||
}
|
||||
|
||||
uint16_t ROW::_adjustBackward(uint16_t column) const noexcept
|
||||
{
|
||||
// Safety: This is a little bit more dangerous. The first column is supposed
|
||||
// to never be a trailer and so this loop should exit if column == 0.
|
||||
for (; _uncheckedIsTrailer(column); --column)
|
||||
{
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
uint16_t ROW::_adjustForward(uint16_t column) const noexcept
|
||||
{
|
||||
// Safety: This is a little bit more dangerous. The last column is supposed
|
||||
// to never be a trailer and so this loop should exit if column == _columnCount.
|
||||
for (; _uncheckedIsTrailer(column); ++column)
|
||||
{
|
||||
}
|
||||
return column;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - clears char data in column in row
|
||||
// Arguments:
|
||||
@@ -388,20 +311,16 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType c
|
||||
}
|
||||
break;
|
||||
case DbcsAttribute::Trailing:
|
||||
// Handling the trailing half of wide chars ensures that we correctly restore
|
||||
// wide characters when a user backs up and restores the viewport via CHAR_INFOs.
|
||||
if (fillingFirstColumn)
|
||||
{
|
||||
// The wide char doesn't fit. Pad with whitespace.
|
||||
// Ignore the character. There's no correct alternative way to handle this situation.
|
||||
ClearCell(currentIndex);
|
||||
}
|
||||
else if (it.Position() == 0)
|
||||
else
|
||||
{
|
||||
// A common way to back up and restore the buffer is via `ReadConsoleOutputW` and
|
||||
// `WriteConsoleOutputW` respectively. But the area might bisect/intersect/clip wide characters and
|
||||
// only backup either their leading or trailing half. In general, in the rest of conhost, we're
|
||||
// throwing away the trailing half of all `CHAR_INFO`s (during text rendering, as well as during
|
||||
// `ReadConsoleOutputW`), so to make this code behave the same and prevent surprises, we need to
|
||||
// make sure to only look at the trailer if it's the first `CHAR_INFO` the user is trying to write.
|
||||
ReplaceCharacters(currentIndex - 1, 2, chars);
|
||||
}
|
||||
++it;
|
||||
@@ -441,9 +360,10 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType c
|
||||
return it;
|
||||
}
|
||||
|
||||
void ROW::SetAttrToEnd(const til::CoordType columnBegin, const TextAttribute attr)
|
||||
bool ROW::SetAttrToEnd(const til::CoordType columnBegin, const TextAttribute attr)
|
||||
{
|
||||
_attr.replace(_clampedColumnInclusive(columnBegin), _attr.size(), attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ROW::ReplaceAttributes(const til::CoordType beginIndex, const til::CoordType endIndex, const TextAttribute& newAttr)
|
||||
@@ -451,322 +371,90 @@ void ROW::ReplaceAttributes(const til::CoordType beginIndex, const til::CoordTyp
|
||||
_attr.replace(_clampedColumnInclusive(beginIndex), _clampedColumnInclusive(endIndex), newAttr);
|
||||
}
|
||||
|
||||
[[msvc::forceinline]] ROW::WriteHelper::WriteHelper(ROW& row, til::CoordType columnBegin, til::CoordType columnLimit, const std::wstring_view& chars) noexcept :
|
||||
row{ row },
|
||||
chars{ chars }
|
||||
{
|
||||
colBeg = row._clampedColumnInclusive(columnBegin);
|
||||
colLimit = row._clampedColumnInclusive(columnLimit);
|
||||
chBegDirty = row._uncheckedCharOffset(colBeg);
|
||||
colBegDirty = row._adjustBackward(colBeg);
|
||||
leadingSpaces = colBeg - colBegDirty;
|
||||
chBeg = chBegDirty + leadingSpaces;
|
||||
colEnd = colBeg;
|
||||
colEndDirty = 0;
|
||||
charsConsumed = 0;
|
||||
}
|
||||
|
||||
[[msvc::forceinline]] bool ROW::WriteHelper::IsValid() const noexcept
|
||||
{
|
||||
return colBeg < colLimit && !chars.empty();
|
||||
}
|
||||
|
||||
void ROW::ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars)
|
||||
try
|
||||
{
|
||||
WriteHelper h{ *this, columnBegin, _columnCount, chars };
|
||||
if (!h.IsValid())
|
||||
const auto colBeg = _clampedUint16(columnBegin);
|
||||
const auto colEnd = _clampedUint16(columnBegin + width);
|
||||
|
||||
if (colBeg >= colEnd || colEnd > _columnCount || chars.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
h.ReplaceCharacters(width);
|
||||
h.Finish();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Due to this function writing _charOffsets first, then calling _resizeChars (which may throw) and only then finally
|
||||
// filling in _chars, we might end up in a situation were _charOffsets contains offsets outside of the _chars array.
|
||||
// --> Restore this row to a known "okay"-state.
|
||||
Reset(TextAttribute{});
|
||||
throw;
|
||||
}
|
||||
|
||||
[[msvc::forceinline]] void ROW::WriteHelper::ReplaceCharacters(til::CoordType width) noexcept
|
||||
{
|
||||
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + width);
|
||||
if (colEndNew > colLimit)
|
||||
{
|
||||
colEndDirty = colLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
til::at(row._charOffsets, colEnd++) = chBeg;
|
||||
for (; colEnd < colEndNew; ++colEnd)
|
||||
{
|
||||
til::at(row._charOffsets, colEnd) = gsl::narrow_cast<uint16_t>(chBeg | CharOffsetsTrailer);
|
||||
}
|
||||
// Safety:
|
||||
// * colBeg is now [0, _columnCount)
|
||||
// * colEnd is now (colBeg, _columnCount]
|
||||
|
||||
colEndDirty = colEnd;
|
||||
charsConsumed = chars.size();
|
||||
}
|
||||
}
|
||||
|
||||
void ROW::ReplaceText(RowWriteState& state)
|
||||
try
|
||||
{
|
||||
WriteHelper h{ *this, state.columnBegin, state.columnLimit, state.text };
|
||||
if (!h.IsValid())
|
||||
{
|
||||
state.columnEnd = h.colBeg;
|
||||
state.columnBeginDirty = h.colBeg;
|
||||
state.columnEndDirty = h.colBeg;
|
||||
return;
|
||||
}
|
||||
h.ReplaceText();
|
||||
h.Finish();
|
||||
|
||||
state.text = state.text.substr(h.charsConsumed);
|
||||
// Here's why we set `state.columnEnd` to `colLimit` if there's remaining text:
|
||||
// Callers should be able to use `state.columnEnd` as the next cursor position, as well as the parameter for a
|
||||
// follow-up call to ReplaceAttributes(). But if we fail to insert a wide glyph into the last column of a row,
|
||||
// that last cell (which now contains padding whitespace) should get the same attributes as the rest of the
|
||||
// string so that the row looks consistent. This requires us to return `colLimit` instead of `colLimit - 1`.
|
||||
// Additionally, this has the benefit that callers can detect line wrapping by checking `columnEnd >= columnLimit`.
|
||||
state.columnEnd = state.text.empty() ? h.colEnd : h.colLimit;
|
||||
state.columnBeginDirty = h.colBegDirty;
|
||||
state.columnEndDirty = h.colEndDirty;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Reset(TextAttribute{});
|
||||
throw;
|
||||
}
|
||||
|
||||
[[msvc::forceinline]] void ROW::WriteHelper::ReplaceText() noexcept
|
||||
{
|
||||
// This function starts with a fast-pass for ASCII. ASCII is still predominant in technical areas.
|
||||
// Algorithm explanation
|
||||
//
|
||||
// We can infer the "end" from the amount of columns we're given (colLimit - colBeg),
|
||||
// because ASCII is always 1 column wide per character.
|
||||
auto it = chars.begin();
|
||||
const auto end = it + std::min<size_t>(chars.size(), colLimit - colBeg);
|
||||
size_t ch = chBeg;
|
||||
|
||||
while (it != end)
|
||||
{
|
||||
if (*it >= 0x80) [[unlikely]]
|
||||
{
|
||||
_replaceTextUnicode(ch, it);
|
||||
return;
|
||||
}
|
||||
|
||||
til::at(row._charOffsets, colEnd) = gsl::narrow_cast<uint16_t>(ch);
|
||||
++colEnd;
|
||||
++ch;
|
||||
++it;
|
||||
}
|
||||
|
||||
colEndDirty = colEnd;
|
||||
charsConsumed = ch - chBeg;
|
||||
}
|
||||
|
||||
[[msvc::forceinline]] void ROW::WriteHelper::_replaceTextUnicode(size_t ch, std::wstring_view::const_iterator it) noexcept
|
||||
{
|
||||
const auto end = chars.end();
|
||||
|
||||
while (it != end)
|
||||
{
|
||||
unsigned int width = 1;
|
||||
auto ptr = &*it;
|
||||
const auto wch = *ptr;
|
||||
size_t advance = 1;
|
||||
|
||||
++it;
|
||||
|
||||
// Even in our slow-path we can avoid calling IsGlyphFullWidth if the current character is ASCII.
|
||||
// It also allows us to skip the surrogate pair decoding at the same time.
|
||||
if (wch >= 0x80)
|
||||
{
|
||||
if (til::is_surrogate(wch))
|
||||
{
|
||||
if (it != end && til::is_leading_surrogate(wch) && til::is_trailing_surrogate(*it))
|
||||
{
|
||||
advance = 2;
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = &UNICODE_REPLACEMENT;
|
||||
}
|
||||
}
|
||||
|
||||
width = IsGlyphFullWidth({ ptr, advance }) + 1u;
|
||||
}
|
||||
|
||||
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + width);
|
||||
if (colEndNew > colLimit)
|
||||
{
|
||||
colEndDirty = colLimit;
|
||||
charsConsumed = ch - chBeg;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill our char-offset buffer with 1 entry containing the mapping from the
|
||||
// current column (colEnd) to the start of the glyph in the string (ch)...
|
||||
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch);
|
||||
// ...followed by 0-N entries containing an indication that the
|
||||
// columns are just a wide-glyph extension of the preceding one.
|
||||
while (colEnd < colEndNew)
|
||||
{
|
||||
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch | CharOffsetsTrailer);
|
||||
}
|
||||
|
||||
ch += advance;
|
||||
}
|
||||
|
||||
colEndDirty = colEnd;
|
||||
charsConsumed = ch - chBeg;
|
||||
}
|
||||
|
||||
void ROW::CopyTextFrom(RowCopyTextFromState& state)
|
||||
try
|
||||
{
|
||||
auto& source = state.source;
|
||||
const auto sourceColBeg = source._clampedColumnInclusive(state.sourceColumnBegin);
|
||||
const auto sourceColLimit = source._clampedColumnInclusive(state.sourceColumnLimit);
|
||||
std::span<const uint16_t> charOffsets;
|
||||
std::wstring_view chars;
|
||||
|
||||
if (sourceColBeg < sourceColLimit)
|
||||
{
|
||||
charOffsets = source._charOffsets.subspan(sourceColBeg, static_cast<size_t>(sourceColLimit) - sourceColBeg + 1);
|
||||
const auto charsOffset = charOffsets.front() & CharOffsetsMask;
|
||||
// We _are_ using span. But C++ decided that string_view and span aren't convertible.
|
||||
// _chars is a std::span for performance and because it refers to raw, shared memory.
|
||||
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
chars = { source._chars.data() + charsOffset, source._chars.size() - charsOffset };
|
||||
}
|
||||
|
||||
WriteHelper h{ *this, state.columnBegin, state.columnLimit, chars };
|
||||
|
||||
if (!h.IsValid() ||
|
||||
// If we were to copy text from ourselves, we'd overwrite
|
||||
// our _charOffsets and break Finish() which reads from it.
|
||||
this == &state.source ||
|
||||
// Any valid charOffsets array is at least 2 elements long (the 1st element is the start offset and the 2nd
|
||||
// element is the length of the first glyph) and begins/ends with a non-trailer offset. We don't really
|
||||
// need to test for the end offset, since `WriteHelper::WriteWithOffsets` already takes care of that.
|
||||
charOffsets.size() < 2 || WI_IsFlagSet(charOffsets.front(), CharOffsetsTrailer))
|
||||
{
|
||||
state.columnEnd = h.colBeg;
|
||||
state.columnBeginDirty = h.colBeg;
|
||||
state.columnEndDirty = h.colBeg;
|
||||
state.sourceColumnEnd = source._columnCount;
|
||||
return;
|
||||
}
|
||||
|
||||
h.CopyTextFrom(charOffsets);
|
||||
h.Finish();
|
||||
|
||||
// state.columnEnd is computed identical to ROW::ReplaceText. Check it out for more information.
|
||||
state.columnEnd = h.charsConsumed == chars.size() ? h.colEnd : h.colLimit;
|
||||
state.columnBeginDirty = h.colBegDirty;
|
||||
state.columnEndDirty = h.colEndDirty;
|
||||
state.sourceColumnEnd = sourceColBeg + h.colEnd - h.colBeg;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Reset(TextAttribute{});
|
||||
throw;
|
||||
}
|
||||
|
||||
[[msvc::forceinline]] void ROW::WriteHelper::CopyTextFrom(const std::span<const uint16_t>& charOffsets) noexcept
|
||||
{
|
||||
// Since our `charOffsets` input is already in columns (just like the `ROW::_charOffsets`),
|
||||
// we can directly look up the end char-offset, but...
|
||||
const auto colEndDirtyInput = std::min(gsl::narrow_cast<uint16_t>(colLimit - colBeg), gsl::narrow<uint16_t>(charOffsets.size() - 1));
|
||||
|
||||
// ...since the colLimit might intersect with a wide glyph in `charOffset`, we need to adjust our input-colEnd.
|
||||
auto colEndInput = colEndDirtyInput;
|
||||
for (; WI_IsFlagSet(til::at(charOffsets, colEndInput), CharOffsetsTrailer); --colEndInput)
|
||||
{
|
||||
}
|
||||
|
||||
const auto baseOffset = til::at(charOffsets, 0);
|
||||
const auto endOffset = til::at(charOffsets, colEndInput);
|
||||
const auto inToOutOffset = gsl::narrow_cast<uint16_t>(chBeg - baseOffset);
|
||||
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
const auto dst = row._charOffsets.data() + colEnd;
|
||||
|
||||
_copyOffsets(dst, charOffsets.data(), colEndInput, inToOutOffset);
|
||||
|
||||
colEnd += colEndInput;
|
||||
colEndDirty = gsl::narrow_cast<uint16_t>(colBeg + colEndDirtyInput);
|
||||
charsConsumed = endOffset - baseOffset;
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
[[msvc::forceinline]] void ROW::WriteHelper::_copyOffsets(uint16_t* __restrict dst, const uint16_t* __restrict src, uint16_t size, uint16_t offset) noexcept
|
||||
{
|
||||
__assume(src != nullptr);
|
||||
__assume(dst != nullptr);
|
||||
|
||||
// All tested compilers (including MSVC) will neatly unroll and vectorize
|
||||
// this loop, which is why it's written in this particular way.
|
||||
for (const auto end = src + size; src != end; ++src, ++dst)
|
||||
{
|
||||
const uint16_t ch = *src;
|
||||
const uint16_t off = ch & CharOffsetsMask;
|
||||
const uint16_t trailer = ch & CharOffsetsTrailer;
|
||||
const uint16_t newOff = off + offset;
|
||||
*dst = newOff | trailer;
|
||||
}
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
[[msvc::forceinline]] void ROW::WriteHelper::Finish()
|
||||
{
|
||||
colEndDirty = row._adjustForward(colEndDirty);
|
||||
|
||||
const uint16_t trailingSpaces = colEndDirty - colEnd;
|
||||
const auto chEndDirtyOld = row._uncheckedCharOffset(colEndDirty);
|
||||
const auto chEndDirty = chBegDirty + charsConsumed + leadingSpaces + trailingSpaces;
|
||||
|
||||
if (chEndDirty != chEndDirtyOld)
|
||||
{
|
||||
row._resizeChars(colEndDirty, chBegDirty, chEndDirty, chEndDirtyOld);
|
||||
}
|
||||
|
||||
{
|
||||
// std::copy_n compiles to memmove. We can do better. It also gets rid of an extra branch,
|
||||
// because std::copy_n avoids calling memmove if the count is 0. It's never 0 for us.
|
||||
const auto itBeg = row._chars.begin() + chBeg;
|
||||
memcpy(&*itBeg, chars.data(), charsConsumed * sizeof(wchar_t));
|
||||
|
||||
if (leadingSpaces)
|
||||
{
|
||||
fill_n_small(row._chars.begin() + chBegDirty, leadingSpaces, L' ');
|
||||
iota_n(row._charOffsets.begin() + colBegDirty, leadingSpaces, chBegDirty);
|
||||
}
|
||||
if (trailingSpaces)
|
||||
{
|
||||
fill_n_small(itBeg + charsConsumed, trailingSpaces, L' ');
|
||||
iota_n(row._charOffsets.begin() + colEnd, trailingSpaces, gsl::narrow_cast<uint16_t>(chBeg + charsConsumed));
|
||||
}
|
||||
}
|
||||
|
||||
// This updates `_doubleBytePadded` whenever we write the last column in the row. `_doubleBytePadded` tells our text
|
||||
// reflow algorithm whether it should ignore the last column. This is important when writing wide characters into
|
||||
// the terminal: If the last wide character in a row only fits partially, we should render whitespace, but
|
||||
// during text reflow pretend as if no whitespace exists. After all, the user didn't write any whitespace there.
|
||||
// Task:
|
||||
// Replace the characters in cells [colBeg, colEnd) with a single `width`-wide glyph consisting of `chars`.
|
||||
//
|
||||
// The way this is written, it'll set `_doubleBytePadded` to `true` no matter whether a wide character didn't fit,
|
||||
// or if the last 2 columns contain a wide character and a narrow character got written into the left half of it.
|
||||
// In both cases `trailingSpaces` is 1 and fills the last column and `_doubleBytePadded` will be `true`.
|
||||
if (colEndDirty == row._columnCount)
|
||||
// Problem:
|
||||
// Imagine that we have the following ROW contents:
|
||||
// "xxyyzz"
|
||||
// xx, yy, zz are 2 cell wide glyphs. We want to insert a 2 cell wide glyph ww at colBeg 1:
|
||||
// ^^
|
||||
// ww
|
||||
// An incorrect result would be:
|
||||
// "xwwyzz"
|
||||
// The half cut off x and y glyph wouldn't make much sense, so we need to fill them with whitespace:
|
||||
// " ww zz"
|
||||
//
|
||||
// Solution:
|
||||
// Given the range we want to replace [colBeg, colEnd), we "extend" it to encompass leading (preceding)
|
||||
// and trailing wide glyphs we partially overwrite resulting in the range [colExtBeg, colExtEnd), where
|
||||
// colExtBeg <= colBeg and colExtEnd >= colEnd. In other words, the to be replaced range has been "extended".
|
||||
// The amount of leading whitespace we need to insert is thus colBeg - colExtBeg
|
||||
// and the amount of trailing whitespace colExtEnd - colEnd.
|
||||
|
||||
// Extend range downwards (leading whitespace)
|
||||
uint16_t colExtBeg = colBeg;
|
||||
// Safety: colExtBeg is [0, _columnCount], because colBeg is.
|
||||
const uint16_t chExtBeg = _uncheckedCharOffset(colExtBeg);
|
||||
// Safety: colExtBeg remains [0, _columnCount] due to colExtBeg != 0.
|
||||
for (; colExtBeg != 0 && _uncheckedIsTrailer(colExtBeg); --colExtBeg)
|
||||
{
|
||||
row.SetDoubleBytePadded(colEnd < row._columnCount);
|
||||
}
|
||||
|
||||
// Extend range upwards (trailing whitespace)
|
||||
uint16_t colExtEnd = colEnd;
|
||||
// Safety: colExtEnd cannot be incremented past _columnCount, because the last
|
||||
// _charOffset at index _columnCount will never get the CharOffsetsTrailer flag.
|
||||
for (; _uncheckedIsTrailer(colExtEnd); ++colExtEnd)
|
||||
{
|
||||
}
|
||||
// Safety: After the previous loop colExtEnd is [0, _columnCount].
|
||||
const uint16_t chExtEnd = _uncheckedCharOffset(colExtEnd);
|
||||
|
||||
const uint16_t leadingSpaces = colBeg - colExtBeg;
|
||||
const uint16_t trailingSpaces = colExtEnd - colEnd;
|
||||
const size_t chExtEndNew = chars.size() + leadingSpaces + trailingSpaces + chExtBeg;
|
||||
|
||||
if (chExtEndNew != chExtEnd)
|
||||
{
|
||||
_resizeChars(colExtEnd, chExtBeg, chExtEnd, chExtEndNew);
|
||||
}
|
||||
|
||||
// Add leading/trailing whitespace and copy chars
|
||||
{
|
||||
auto it = _chars.begin() + chExtBeg;
|
||||
it = fill_n_small(it, leadingSpaces, L' ');
|
||||
it = copy_n_small(chars.begin(), chars.size(), it);
|
||||
it = fill_n_small(it, trailingSpaces, L' ');
|
||||
}
|
||||
// Update char offsets with leading/trailing whitespace and the chars columns.
|
||||
{
|
||||
auto chPos = chExtBeg;
|
||||
auto it = _charOffsets.begin() + colExtBeg;
|
||||
|
||||
it = iota_n_mut(it, leadingSpaces, chPos);
|
||||
|
||||
*it++ = chPos;
|
||||
it = fill_small(it, _charOffsets.begin() + colEnd, gsl::narrow_cast<uint16_t>(chPos | CharOffsetsTrailer));
|
||||
chPos = gsl::narrow_cast<uint16_t>(chPos + chars.size());
|
||||
|
||||
it = iota_n_mut(it, trailingSpaces, chPos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,15 +462,15 @@ catch (...)
|
||||
// as it reallocates the backing buffer and shifts the char offsets.
|
||||
// The parameters are difficult to explain, but their names are identical to
|
||||
// local variables in ReplaceCharacters() which I've attempted to document there.
|
||||
void ROW::_resizeChars(uint16_t colEndDirty, uint16_t chBegDirty, size_t chEndDirty, uint16_t chEndDirtyOld)
|
||||
void ROW::_resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew)
|
||||
{
|
||||
const auto diff = chEndDirty - chEndDirtyOld;
|
||||
const auto diff = chExtEndNew - chExtEnd;
|
||||
const auto currentLength = _charSize();
|
||||
const auto newLength = currentLength + diff;
|
||||
|
||||
if (newLength <= _chars.size())
|
||||
{
|
||||
std::copy_n(_chars.begin() + chEndDirtyOld, currentLength - chEndDirtyOld, _chars.begin() + chEndDirty);
|
||||
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, _chars.begin() + chExtEndNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -792,14 +480,14 @@ void ROW::_resizeChars(uint16_t colEndDirty, uint16_t chBegDirty, size_t chEndDi
|
||||
auto charsHeap = std::make_unique_for_overwrite<wchar_t[]>(newCapacity);
|
||||
const std::span chars{ charsHeap.get(), newCapacity };
|
||||
|
||||
std::copy_n(_chars.begin(), chBegDirty, chars.begin());
|
||||
std::copy_n(_chars.begin() + chEndDirtyOld, currentLength - chEndDirtyOld, chars.begin() + chEndDirty);
|
||||
std::copy_n(_chars.begin(), chExtBeg, chars.begin());
|
||||
std::copy_n(_chars.begin() + chExtEnd, currentLength - chExtEnd, chars.begin() + chExtEndNew);
|
||||
|
||||
_charsHeap = std::move(charsHeap);
|
||||
_chars = chars;
|
||||
}
|
||||
|
||||
auto it = _charOffsets.begin() + colEndDirty;
|
||||
auto it = _charOffsets.begin() + colExtEnd;
|
||||
const auto end = _charOffsets.end();
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
@@ -807,11 +495,6 @@ void ROW::_resizeChars(uint16_t colEndDirty, uint16_t chBegDirty, size_t chEndDi
|
||||
}
|
||||
}
|
||||
|
||||
til::small_rle<TextAttribute, uint16_t, 1>& ROW::Attributes() noexcept
|
||||
{
|
||||
return _attr;
|
||||
}
|
||||
|
||||
const til::small_rle<TextAttribute, uint16_t, 1>& ROW::Attributes() const noexcept
|
||||
{
|
||||
return _attr;
|
||||
@@ -840,12 +523,6 @@ uint16_t ROW::size() const noexcept
|
||||
return _columnCount;
|
||||
}
|
||||
|
||||
til::CoordType ROW::LineRenditionColumns() const noexcept
|
||||
{
|
||||
const auto scale = _lineRendition != LineRendition::SingleWidth ? 1 : 0;
|
||||
return _columnCount >> scale;
|
||||
}
|
||||
|
||||
til::CoordType ROW::MeasureLeft() const noexcept
|
||||
{
|
||||
const auto text = GetText();
|
||||
@@ -947,17 +624,6 @@ std::wstring_view ROW::GetText() const noexcept
|
||||
return { _chars.data(), _charSize() };
|
||||
}
|
||||
|
||||
std::wstring_view ROW::GetText(til::CoordType columnBegin, til::CoordType columnEnd) const noexcept
|
||||
{
|
||||
const til::CoordType columns = _columnCount;
|
||||
const auto colBeg = std::max(0, std::min(columns, columnBegin));
|
||||
const auto colEnd = std::max(colBeg, std::min(columns, columnEnd));
|
||||
const size_t chBeg = _uncheckedCharOffset(gsl::narrow_cast<size_t>(colBeg));
|
||||
const size_t chEnd = _uncheckedCharOffset(gsl::narrow_cast<size_t>(colEnd));
|
||||
#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
return { _chars.data() + chBeg, chEnd - chBeg };
|
||||
}
|
||||
|
||||
DelimiterClass ROW::DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept
|
||||
{
|
||||
const auto col = _clampedColumn(column);
|
||||
@@ -1011,13 +677,11 @@ uint16_t ROW::_charSize() const noexcept
|
||||
// Safety: col must be [0, _columnCount].
|
||||
uint16_t ROW::_uncheckedCharOffset(size_t col) const noexcept
|
||||
{
|
||||
assert(col < _charOffsets.size());
|
||||
return til::at(_charOffsets, col) & CharOffsetsMask;
|
||||
}
|
||||
|
||||
// Safety: col must be [0, _columnCount].
|
||||
bool ROW::_uncheckedIsTrailer(size_t col) const noexcept
|
||||
{
|
||||
assert(col < _charOffsets.size());
|
||||
return WI_IsFlagSet(til::at(_charOffsets, col), CharOffsetsTrailer);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- Row.hpp
|
||||
|
||||
Abstract:
|
||||
- data structure for information associated with one row of screen buffer
|
||||
|
||||
Author(s):
|
||||
- Michael Niksa (miniksa) 10-Apr-2014
|
||||
- Paul Campbell (paulcam) 10-Apr-2014
|
||||
|
||||
Revision History:
|
||||
- From components of output.h/.c
|
||||
by Therese Stowell (ThereseS) 1990-1991
|
||||
- Pulled into its own file from textBuffer.hpp/cpp (AustDi, 2017)
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include <til/rle.h>
|
||||
|
||||
#include "LineRendition.hpp"
|
||||
#include "OutputCell.hpp"
|
||||
#include "OutputCellIterator.hpp"
|
||||
|
||||
class ROW;
|
||||
class TextBuffer;
|
||||
|
||||
enum class DelimiterClass
|
||||
@@ -19,123 +37,47 @@ enum class DelimiterClass
|
||||
RegularChar
|
||||
};
|
||||
|
||||
struct RowWriteState
|
||||
{
|
||||
// The text you want to write into the given ROW. When ReplaceText() returns,
|
||||
// this is updated to remove all text from the beginning that was successfully written.
|
||||
std::wstring_view text; // IN/OUT
|
||||
// The column at which to start writing.
|
||||
til::CoordType columnBegin = 0; // IN
|
||||
// The first column which should not be written to anymore.
|
||||
til::CoordType columnLimit = til::CoordTypeMax; // IN
|
||||
|
||||
// The column 1 past the last glyph that was successfully written into the row. If you need to call
|
||||
// ReplaceAttributes() to colorize the written range, etc., this is the columnEnd parameter you want.
|
||||
// If you want to continue writing where you left off, this is also the next columnBegin parameter.
|
||||
til::CoordType columnEnd = 0; // OUT
|
||||
// The first column that got modified by this write operation. In case that the first glyph we write overwrites
|
||||
// the trailing half of a wide glyph, leadingSpaces will be 1 and this value will be 1 less than colBeg.
|
||||
til::CoordType columnBeginDirty = 0; // OUT
|
||||
// This is 1 past the last column that was modified and will be 1 past columnEnd if we overwrote
|
||||
// the leading half of a wide glyph and had to fill the trailing half with whitespace.
|
||||
til::CoordType columnEndDirty = 0; // OUT
|
||||
};
|
||||
|
||||
struct RowCopyTextFromState
|
||||
{
|
||||
// The row to copy text from.
|
||||
const ROW& source; // IN
|
||||
// The column at which to start writing.
|
||||
til::CoordType columnBegin = 0; // IN
|
||||
// The first column which should not be written to anymore.
|
||||
til::CoordType columnLimit = til::CoordTypeMax; // IN
|
||||
// The column at which to start reading from source.
|
||||
til::CoordType sourceColumnBegin = 0; // IN
|
||||
// The first column which should not be read from anymore.
|
||||
til::CoordType sourceColumnLimit = til::CoordTypeMax; // IN
|
||||
|
||||
til::CoordType columnEnd = 0; // OUT
|
||||
// The first column that got modified by this write operation. In case that the first glyph we write overwrites
|
||||
// the trailing half of a wide glyph, leadingSpaces will be 1 and this value will be 1 less than colBeg.
|
||||
til::CoordType columnBeginDirty = 0; // OUT
|
||||
// This is 1 past the last column that was modified and will be 1 past columnEnd if we overwrote
|
||||
// the leading half of a wide glyph and had to fill the trailing half with whitespace.
|
||||
til::CoordType columnEndDirty = 0; // OUT
|
||||
// This is 1 past the last column that was read from.
|
||||
til::CoordType sourceColumnEnd = 0; // OUT
|
||||
};
|
||||
|
||||
class ROW final
|
||||
{
|
||||
public:
|
||||
// The implicit agreement between ROW and TextBuffer is that the `charsBuffer` and `charOffsetsBuffer`
|
||||
// arrays have a minimum alignment of 16 Bytes and a size of `rowWidth+1`. The former is used to
|
||||
// implement Reset() efficiently via SIMD and the latter is used to store the past-the-end offset
|
||||
// into the `charsBuffer`. Even though the `charsBuffer` could be only `rowWidth` large we need them
|
||||
// to be the same size so that the SIMD code can process both arrays in the same loop simultaneously.
|
||||
// This wastes up to 5.8% memory but increases overall scrolling performance by around 40%.
|
||||
// These methods exists to make this agreement explicit and serve as a reminder.
|
||||
//
|
||||
// TextBuffer calculates the distance in bytes between two ROWs (_bufferRowStride) as the sum of these values.
|
||||
// As such it's important that we return sizes with a minimum alignment of alignof(ROW).
|
||||
static constexpr size_t CalculateRowSize() noexcept
|
||||
{
|
||||
return (sizeof(ROW) + 15) & ~15;
|
||||
}
|
||||
static constexpr size_t CalculateCharsBufferSize(size_t columns) noexcept
|
||||
{
|
||||
return (columns * sizeof(wchar_t) + 16) & ~15;
|
||||
}
|
||||
static constexpr size_t CalculateCharOffsetsBufferSize(size_t columns) noexcept
|
||||
{
|
||||
return (columns * sizeof(uint16_t) + 16) & ~15;
|
||||
}
|
||||
|
||||
ROW() = default;
|
||||
ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);
|
||||
|
||||
ROW(const ROW& other) = delete;
|
||||
ROW& operator=(const ROW& other) = delete;
|
||||
|
||||
ROW(ROW&& other) = default;
|
||||
explicit ROW(ROW&& other) = default;
|
||||
ROW& operator=(ROW&& other) = default;
|
||||
|
||||
friend void swap(ROW& lhs, ROW& rhs) noexcept;
|
||||
|
||||
void SetWrapForced(const bool wrap) noexcept;
|
||||
bool WasWrapForced() const noexcept;
|
||||
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept;
|
||||
bool WasDoubleBytePadded() const noexcept;
|
||||
void SetLineRendition(const LineRendition lineRendition) noexcept;
|
||||
LineRendition GetLineRendition() const noexcept;
|
||||
uint16_t GetLineWidth() const noexcept;
|
||||
|
||||
void Reset(const TextAttribute& attr) noexcept;
|
||||
void Reset(const TextAttribute& attr);
|
||||
void Resize(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);
|
||||
void TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth);
|
||||
void CopyFrom(const ROW& source);
|
||||
|
||||
til::CoordType NavigateToPrevious(til::CoordType column) const noexcept;
|
||||
til::CoordType NavigateToNext(til::CoordType column) const noexcept;
|
||||
|
||||
void ClearCell(til::CoordType column);
|
||||
OutputCellIterator WriteCells(OutputCellIterator it, til::CoordType columnBegin, std::optional<bool> wrap = std::nullopt, std::optional<til::CoordType> limitRight = std::nullopt);
|
||||
void SetAttrToEnd(til::CoordType columnBegin, TextAttribute attr);
|
||||
bool SetAttrToEnd(til::CoordType columnBegin, TextAttribute attr);
|
||||
void ReplaceAttributes(til::CoordType beginIndex, til::CoordType endIndex, const TextAttribute& newAttr);
|
||||
void ReplaceCharacters(til::CoordType columnBegin, til::CoordType width, const std::wstring_view& chars);
|
||||
void ReplaceText(RowWriteState& state);
|
||||
void CopyTextFrom(RowCopyTextFromState& state);
|
||||
|
||||
til::small_rle<TextAttribute, uint16_t, 1>& Attributes() noexcept;
|
||||
const til::small_rle<TextAttribute, uint16_t, 1>& Attributes() const noexcept;
|
||||
TextAttribute GetAttrByColumn(til::CoordType column) const;
|
||||
std::vector<uint16_t> GetHyperlinks() const;
|
||||
uint16_t size() const noexcept;
|
||||
til::CoordType LineRenditionColumns() const noexcept;
|
||||
til::CoordType MeasureLeft() const noexcept;
|
||||
til::CoordType MeasureRight() const noexcept;
|
||||
bool ContainsText() const noexcept;
|
||||
std::wstring_view GlyphAt(til::CoordType column) const noexcept;
|
||||
DbcsAttribute DbcsAttrAt(til::CoordType column) const noexcept;
|
||||
std::wstring_view GetText() const noexcept;
|
||||
std::wstring_view GetText(til::CoordType columnBegin, til::CoordType columnEnd) const noexcept;
|
||||
DelimiterClass DelimiterClassAt(til::CoordType column, const std::wstring_view& wordDelimiters) const noexcept;
|
||||
|
||||
auto AttrBegin() const noexcept { return _attr.begin(); }
|
||||
@@ -147,52 +89,6 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
// WriteHelper exists because other forms of abstracting this functionality away (like templates with lambdas)
|
||||
// where only very poorly optimized by MSVC as it failed to inline the templates.
|
||||
struct WriteHelper
|
||||
{
|
||||
explicit WriteHelper(ROW& row, til::CoordType columnBegin, til::CoordType columnLimit, const std::wstring_view& chars) noexcept;
|
||||
bool IsValid() const noexcept;
|
||||
void ReplaceCharacters(til::CoordType width) noexcept;
|
||||
void ReplaceText() noexcept;
|
||||
void _replaceTextUnicode(size_t ch, std::wstring_view::const_iterator it) noexcept;
|
||||
void CopyTextFrom(const std::span<const uint16_t>& charOffsets) noexcept;
|
||||
static void _copyOffsets(uint16_t* dst, const uint16_t* src, uint16_t size, uint16_t offset) noexcept;
|
||||
void Finish();
|
||||
|
||||
// Parent pointer.
|
||||
ROW& row;
|
||||
// The text given by the caller.
|
||||
const std::wstring_view& chars;
|
||||
|
||||
// This is the same as the columnBegin parameter for ReplaceText(), etc.,
|
||||
// but clamped to a valid range via _clampedColumnInclusive.
|
||||
uint16_t colBeg;
|
||||
// This is the same as the columnLimit parameter for ReplaceText(), etc.,
|
||||
// but clamped to a valid range via _clampedColumnInclusive.
|
||||
uint16_t colLimit;
|
||||
|
||||
// The column 1 past the last glyph that was successfully written into the row. If you need to call
|
||||
// ReplaceAttributes() to colorize the written range, etc., this is the columnEnd parameter you want.
|
||||
// If you want to continue writing where you left off, this is also the next columnBegin parameter.
|
||||
uint16_t colEnd;
|
||||
// The first column that got modified by this write operation. In case that the first glyph we write overwrites
|
||||
// the trailing half of a wide glyph, leadingSpaces will be 1 and this value will be 1 less than colBeg.
|
||||
uint16_t colBegDirty;
|
||||
// Similar to dirtyBeg, this is 1 past the last column that was modified and will be 1 past colEnd if
|
||||
// we overwrote the leading half of a wide glyph and had to fill the trailing half with whitespace.
|
||||
uint16_t colEndDirty;
|
||||
// The offset in ROW::chars at which we start writing the contents of WriteHelper::chars.
|
||||
uint16_t chBeg;
|
||||
// The offset at which we start writing leadingSpaces-many whitespaces.
|
||||
uint16_t chBegDirty;
|
||||
// The same as `colBeg - colBegDirty`. This is the amount of whitespace
|
||||
// we write at chBegDirty, before the actual WriteHelper::chars content.
|
||||
uint16_t leadingSpaces;
|
||||
// The amount of characters copied from WriteHelper::chars.
|
||||
size_t charsConsumed;
|
||||
};
|
||||
|
||||
// To simplify the detection of wide glyphs, we don't just store the simple character offset as described
|
||||
// for _charOffsets. Instead we use the most significant bit to indicate whether any column is the
|
||||
// trailing half of a wide glyph. This simplifies many implementation details via _uncheckedIsTrailer.
|
||||
@@ -206,16 +102,13 @@ private:
|
||||
template<typename T>
|
||||
constexpr uint16_t _clampedColumnInclusive(T v) const noexcept;
|
||||
|
||||
uint16_t _adjustBackward(uint16_t column) const noexcept;
|
||||
uint16_t _adjustForward(uint16_t column) const noexcept;
|
||||
|
||||
wchar_t _uncheckedChar(size_t off) const noexcept;
|
||||
uint16_t _charSize() const noexcept;
|
||||
uint16_t _uncheckedCharOffset(size_t col) const noexcept;
|
||||
bool _uncheckedIsTrailer(size_t col) const noexcept;
|
||||
|
||||
void _init() noexcept;
|
||||
void _resizeChars(uint16_t colEndDirty, uint16_t chBegDirty, size_t chEndDirty, uint16_t chEndDirtyOld);
|
||||
void _resizeChars(uint16_t colExtEnd, uint16_t chExtBeg, uint16_t chExtEnd, size_t chExtEndNew);
|
||||
|
||||
// These fields are a bit "wasteful", but it makes all this a bit more robust against
|
||||
// programming errors during initial development (which is when this comment was written).
|
||||
|
||||
@@ -64,7 +64,7 @@ bool TextColor::CanBeBrightened() const noexcept
|
||||
|
||||
bool TextColor::IsLegacy() const noexcept
|
||||
{
|
||||
return (IsIndex16() || IsIndex256()) && _index < 16;
|
||||
return IsIndex16() || (IsIndex256() && _index < 16);
|
||||
}
|
||||
|
||||
bool TextColor::IsIndex16() const noexcept
|
||||
@@ -82,11 +82,6 @@ bool TextColor::IsDefault() const noexcept
|
||||
return _meta == ColorType::IsDefault;
|
||||
}
|
||||
|
||||
bool TextColor::IsDefaultOrLegacy() const noexcept
|
||||
{
|
||||
return _meta != ColorType::IsRgb && _index < 16;
|
||||
}
|
||||
|
||||
bool TextColor::IsRgb() const noexcept
|
||||
{
|
||||
return _meta == ColorType::IsRgb;
|
||||
|
||||
@@ -37,14 +37,12 @@ Revision History:
|
||||
#include "WexTestClass.h"
|
||||
#endif
|
||||
|
||||
// The enum values being in this particular order allows the compiler to do some useful optimizations,
|
||||
// like simplifying `IsIndex16() || IsIndex256()` into a simple range check without branching.
|
||||
enum class ColorType : BYTE
|
||||
{
|
||||
IsDefault,
|
||||
IsIndex16,
|
||||
IsIndex256,
|
||||
IsRgb
|
||||
IsIndex256 = 0x0,
|
||||
IsIndex16 = 0x1,
|
||||
IsDefault = 0x2,
|
||||
IsRgb = 0x3
|
||||
};
|
||||
|
||||
enum class ColorAlias : size_t
|
||||
@@ -123,7 +121,6 @@ public:
|
||||
bool IsIndex16() const noexcept;
|
||||
bool IsIndex256() const noexcept;
|
||||
bool IsDefault() const noexcept;
|
||||
bool IsDefaultOrLegacy() const noexcept;
|
||||
bool IsRgb() const noexcept;
|
||||
|
||||
void SetColor(const COLORREF rgbColor) noexcept;
|
||||
|
||||
@@ -286,9 +286,9 @@ void Cursor::CopyProperties(const Cursor& OtherCursor) noexcept
|
||||
_cursorType = OtherCursor._cursorType;
|
||||
}
|
||||
|
||||
void Cursor::DelayEOLWrap() noexcept
|
||||
void Cursor::DelayEOLWrap(const til::point coordDelayedAt) noexcept
|
||||
{
|
||||
_coordDelayedAt = _cPosition;
|
||||
_coordDelayedAt = coordDelayedAt;
|
||||
_fDelayedEolWrap = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
|
||||
void CopyProperties(const Cursor& OtherCursor) noexcept;
|
||||
|
||||
void DelayEOLWrap() noexcept;
|
||||
void DelayEOLWrap(const til::point coordDelayedAt) noexcept;
|
||||
void ResetDelayEOLWrap() noexcept;
|
||||
til::point GetDelayedAtPosition() const noexcept;
|
||||
bool IsDelayedEOLWrap() const noexcept;
|
||||
|
||||
@@ -1,7 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- precomp.h
|
||||
|
||||
Abstract:
|
||||
- Contains external headers to include in the precompile phase of console build process.
|
||||
- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
|
||||
--*/
|
||||
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibraryIncludes.h>
|
||||
// clang-format off
|
||||
|
||||
#include <unicode.hpp>
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#include "LibraryIncludes.h"
|
||||
|
||||
#pragma warning(push)
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#define NOMCX
|
||||
#define NOHELP
|
||||
#define NOCOMM
|
||||
#endif
|
||||
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
#include <intsafe.h>
|
||||
|
||||
// private dependencies
|
||||
#include "../inc/unicode.hpp"
|
||||
#pragma warning(pop)
|
||||
|
||||
// clang-format on
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -72,22 +72,14 @@ public:
|
||||
const UINT cursorSize,
|
||||
const bool isActiveBuffer,
|
||||
Microsoft::Console::Render::Renderer& renderer);
|
||||
|
||||
TextBuffer(const TextBuffer&) = delete;
|
||||
TextBuffer(TextBuffer&&) = delete;
|
||||
TextBuffer& operator=(const TextBuffer&) = delete;
|
||||
TextBuffer& operator=(TextBuffer&&) = delete;
|
||||
|
||||
~TextBuffer();
|
||||
TextBuffer(const TextBuffer& a) = delete;
|
||||
|
||||
// Used for duplicating properties to another text buffer
|
||||
void CopyProperties(const TextBuffer& OtherBuffer) noexcept;
|
||||
|
||||
// row manipulation
|
||||
ROW& GetScratchpadRow();
|
||||
ROW& GetScratchpadRow(const TextAttribute& attributes);
|
||||
const ROW& GetRowByOffset(til::CoordType index) const;
|
||||
ROW& GetRowByOffset(til::CoordType index);
|
||||
const ROW& GetRowByOffset(const til::CoordType index) const noexcept;
|
||||
ROW& GetRowByOffset(const til::CoordType index) noexcept;
|
||||
|
||||
TextBufferCellIterator GetCellDataAt(const til::point at) const;
|
||||
TextBufferCellIterator GetCellLineDataAt(const til::point at) const;
|
||||
@@ -97,10 +89,6 @@ public:
|
||||
TextBufferTextIterator GetTextDataAt(const til::point at, const Microsoft::Console::Types::Viewport limit) const;
|
||||
|
||||
// Text insertion functions
|
||||
static void ConsumeGrapheme(std::wstring_view& chars) noexcept;
|
||||
void Write(til::CoordType row, const TextAttribute& attributes, RowWriteState& state);
|
||||
void FillRect(const til::rect& rect, const std::wstring_view& fill, const TextAttribute& attributes);
|
||||
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt);
|
||||
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt,
|
||||
@@ -112,13 +100,13 @@ public:
|
||||
const std::optional<bool> setWrap = std::nullopt,
|
||||
const std::optional<til::CoordType> limitRight = std::nullopt);
|
||||
|
||||
void InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
||||
void InsertCharacter(const std::wstring_view chars, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
||||
void IncrementCursor();
|
||||
void NewlineCursor();
|
||||
bool InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
||||
bool InsertCharacter(const std::wstring_view chars, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
||||
bool IncrementCursor();
|
||||
bool NewlineCursor();
|
||||
|
||||
// Scroll needs access to this to quickly rotate around the buffer.
|
||||
void IncrementCircularBuffer(const TextAttribute& fillAttributes = {});
|
||||
bool IncrementCircularBuffer(const bool inVtMode = false);
|
||||
|
||||
til::point GetLastNonSpaceCharacter(std::optional<const Microsoft::Console::Types::Viewport> viewOptional = std::nullopt) const;
|
||||
|
||||
@@ -133,22 +121,21 @@ public:
|
||||
|
||||
til::CoordType TotalRowCount() const noexcept;
|
||||
|
||||
const TextAttribute& GetCurrentAttributes() const noexcept;
|
||||
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
|
||||
|
||||
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
|
||||
|
||||
void SetWrapForced(til::CoordType y, bool wrap);
|
||||
void SetCurrentLineRendition(const LineRendition lineRendition, const TextAttribute& fillAttributes);
|
||||
void ResetLineRenditionRange(const til::CoordType startRow, const til::CoordType endRow);
|
||||
LineRendition GetLineRendition(const til::CoordType row) const;
|
||||
bool IsDoubleWidthLine(const til::CoordType row) const;
|
||||
void SetCurrentLineRendition(const LineRendition lineRendition);
|
||||
void ResetLineRenditionRange(const til::CoordType startRow, const til::CoordType endRow) noexcept;
|
||||
LineRendition GetLineRendition(const til::CoordType row) const noexcept;
|
||||
bool IsDoubleWidthLine(const til::CoordType row) const noexcept;
|
||||
|
||||
til::CoordType GetLineWidth(const til::CoordType row) const;
|
||||
til::point ClampPositionWithinLine(const til::point position) const;
|
||||
til::point ScreenToBufferPosition(const til::point position) const;
|
||||
til::point BufferToScreenPosition(const til::point position) const;
|
||||
til::CoordType GetLineWidth(const til::CoordType row) const noexcept;
|
||||
til::point ClampPositionWithinLine(const til::point position) const noexcept;
|
||||
til::point ScreenToBufferPosition(const til::point position) const noexcept;
|
||||
til::point BufferToScreenPosition(const til::point position) const noexcept;
|
||||
|
||||
void Reset() noexcept;
|
||||
void Reset();
|
||||
|
||||
[[nodiscard]] HRESULT ResizeTraditional(const til::size newSize) noexcept;
|
||||
|
||||
@@ -229,27 +216,21 @@ public:
|
||||
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const til::CoordType firstRow, const til::CoordType lastRow) const;
|
||||
|
||||
private:
|
||||
void _reserve(til::size screenBufferSize, const TextAttribute& defaultAttributes);
|
||||
void _commit(const std::byte* row);
|
||||
void _decommit() noexcept;
|
||||
void _construct(const std::byte* until) noexcept;
|
||||
void _destroy() const noexcept;
|
||||
ROW& _getRowByOffsetDirect(size_t offset);
|
||||
til::CoordType _estimateOffsetOfLastCommittedRow() const noexcept;
|
||||
|
||||
void _UpdateSize();
|
||||
void _SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept;
|
||||
til::point _GetPreviousFromCursor() const;
|
||||
void _SetWrapOnCurrentRow();
|
||||
void _AdjustWrapOnCurrentRow(const bool fSet);
|
||||
til::point _GetPreviousFromCursor() const noexcept;
|
||||
void _SetWrapOnCurrentRow() noexcept;
|
||||
void _AdjustWrapOnCurrentRow(const bool fSet) noexcept;
|
||||
// Assist with maintaining proper buffer state for Double Byte character sequences
|
||||
void _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute);
|
||||
bool _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute);
|
||||
bool _AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute);
|
||||
ROW& _GetFirstRow() noexcept;
|
||||
void _ExpandTextRow(til::inclusive_rect& selectionRow) const;
|
||||
DelimiterClass _GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
DelimiterClass _GetDelimiterClassAt(const til::point pos, const std::wstring_view wordDelimiters) const noexcept;
|
||||
til::point _GetWordStartForAccessibility(const til::point target, const std::wstring_view wordDelimiters) const noexcept;
|
||||
til::point _GetWordStartForSelection(const til::point target, const std::wstring_view wordDelimiters) const noexcept;
|
||||
til::point _GetWordEndForAccessibility(const til::point target, const std::wstring_view wordDelimiters, const til::point limit) const;
|
||||
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const;
|
||||
til::point _GetWordEndForSelection(const til::point target, const std::wstring_view wordDelimiters) const noexcept;
|
||||
void _PruneHyperlinks();
|
||||
|
||||
static void _AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text);
|
||||
@@ -263,67 +244,13 @@ private:
|
||||
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
|
||||
size_t _currentPatternId = 0;
|
||||
|
||||
// This block describes the state of the underlying virtual memory buffer that holds all ROWs, text and attributes.
|
||||
// Initially memory is only allocated with MEM_RESERVE to reduce the private working set of conhost.
|
||||
// ROWs are laid out like this in memory:
|
||||
// ROW <-- sizeof(ROW), stores
|
||||
// (padding)
|
||||
// ROW::_charsBuffer <-- _width * sizeof(wchar_t)
|
||||
// (padding)
|
||||
// ROW::_charOffsets <-- (_width + 1) * sizeof(uint16_t)
|
||||
// (padding)
|
||||
// ...
|
||||
// Padding may exist for alignment purposes.
|
||||
//
|
||||
// The base (start) address of the memory arena.
|
||||
wil::unique_virtualalloc_ptr<std::byte> _buffer;
|
||||
// The past-the-end pointer of the memory arena.
|
||||
std::byte* _bufferEnd = nullptr;
|
||||
// The range between _buffer (inclusive) and _commitWatermark (exclusive) is the range of
|
||||
// memory that has already been committed via MEM_COMMIT and contains ready-to-use ROWs.
|
||||
//
|
||||
// The problem is that calling VirtualAlloc(MEM_COMMIT) on each ROW one by one is extremely expensive, which forces
|
||||
// us to commit ROWs in batches and avoid calling it on already committed ROWs. Let's say we commit memory in
|
||||
// batches of 128 ROWs. One option to know whether a ROW has already been committed is to allocate a vector<uint8_t>
|
||||
// of size `(height + 127) / 128` and mark the corresponding slot as 1 if that 128-sized batch has been committed.
|
||||
// That way we know not to commit it again. But ROWs aren't accessed randomly. Instead, they're usually accessed
|
||||
// fairly linearly from row 1 to N. As such we can just commit ROWs up to the point of the highest accessed ROW
|
||||
// plus some read-ahead of 128 ROWs. This is exactly what _commitWatermark stores: The highest accessed ROW plus
|
||||
// some read-ahead. It's the amount of memory that has been committed and is ready to use.
|
||||
//
|
||||
// _commitWatermark will always be a multiple of _bufferRowStride away from _buffer.
|
||||
// In other words, _commitWatermark itself will either point exactly onto the next ROW
|
||||
// that should be committed or be equal to _bufferEnd when all ROWs are committed.
|
||||
std::byte* _commitWatermark = nullptr;
|
||||
// This will MEM_COMMIT 128 rows more than we need, to avoid us from having to call VirtualAlloc too often.
|
||||
// This equates to roughly the following commit chunk sizes at these column counts:
|
||||
// * 80 columns (the usual minimum) = 60KB chunks, 4.1MB buffer at 9001 rows
|
||||
// * 120 columns (the most common) = 80KB chunks, 5.6MB buffer at 9001 rows
|
||||
// * 400 columns (the usual maximum) = 220KB chunks, 15.5MB buffer at 9001 rows
|
||||
// There's probably a better metric than this. (This comment was written when ROW had both,
|
||||
// a _chars array containing text and a _charOffsets array contain column-to-text indices.)
|
||||
static constexpr size_t _commitReadAheadRowCount = 128;
|
||||
// Before TextBuffer was made to use virtual memory it initialized the entire memory arena with the initial
|
||||
// attributes right away. To ensure it continues to work the way it used to, this stores these initial attributes.
|
||||
TextAttribute _initialAttributes;
|
||||
// ROW ---------------+--+--+
|
||||
// (padding) | | v _bufferOffsetChars
|
||||
// ROW::_charsBuffer | |
|
||||
// (padding) | v _bufferOffsetCharOffsets
|
||||
// ROW::_charOffsets |
|
||||
// (padding) v _bufferRowStride
|
||||
size_t _bufferRowStride = 0;
|
||||
size_t _bufferOffsetChars = 0;
|
||||
size_t _bufferOffsetCharOffsets = 0;
|
||||
// The width of the buffer in columns.
|
||||
uint16_t _width = 0;
|
||||
// The height of the buffer in rows, excluding the scratchpad row.
|
||||
uint16_t _height = 0;
|
||||
|
||||
wil::unique_virtualalloc_ptr<std::byte> _charBuffer;
|
||||
std::vector<ROW> _storage;
|
||||
TextAttribute _currentAttributes;
|
||||
til::CoordType _firstRow = 0; // indexes top row (not necessarily 0)
|
||||
|
||||
Cursor _cursor;
|
||||
Microsoft::Console::Types::Viewport _size;
|
||||
|
||||
bool _isActiveBuffer = false;
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ ptrdiff_t TextBufferCellIterator::operator-(const TextBufferCellIterator& it)
|
||||
// - Sets the coordinate position that this iterator will inspect within the text buffer on dereference.
|
||||
// Arguments:
|
||||
// - newPos - The new coordinate position.
|
||||
void TextBufferCellIterator::_SetPos(const til::point newPos)
|
||||
void TextBufferCellIterator::_SetPos(const til::point newPos) noexcept
|
||||
{
|
||||
if (newPos.y != _pos.y)
|
||||
{
|
||||
@@ -315,7 +315,7 @@ void TextBufferCellIterator::_SetPos(const til::point newPos)
|
||||
// - pos - Position inside screen buffer bounds to retrieve row
|
||||
// Return Value:
|
||||
// - Pointer to the underlying CharRow structure
|
||||
const ROW* TextBufferCellIterator::s_GetRow(const TextBuffer& buffer, const til::point pos)
|
||||
const ROW* TextBufferCellIterator::s_GetRow(const TextBuffer& buffer, const til::point pos) noexcept
|
||||
{
|
||||
return &buffer.GetRowByOffset(pos.y);
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ public:
|
||||
til::point Pos() const noexcept;
|
||||
|
||||
protected:
|
||||
void _SetPos(const til::point newPos);
|
||||
void _SetPos(const til::point newPos) noexcept;
|
||||
void _GenerateView() noexcept;
|
||||
static const ROW* s_GetRow(const TextBuffer& buffer, const til::point pos);
|
||||
static const ROW* s_GetRow(const TextBuffer& buffer, const til::point pos) noexcept;
|
||||
|
||||
til::small_rle<TextAttribute, uint16_t, 1>::const_iterator _attrIter;
|
||||
OutputCellView _view;
|
||||
|
||||
@@ -153,8 +153,6 @@
|
||||
<ItemGroup>
|
||||
<FrameworkSdkReference Remove="@(FrameworkSdkReference)" Condition="'%(FrameworkSdkReference.SimpleName)'=='Microsoft.VCLibs'" />
|
||||
<FrameworkSdkPackage Remove="@(FrameworkSdkPackage)" Condition="'%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00' or '%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00.Debug'" />
|
||||
<FrameworkSdkReference Remove="@(FrameworkSdkReference)" Condition="'%(FrameworkSdkReference.SimpleName)'=='Microsoft.VCLibs.Desktop'" />
|
||||
<FrameworkSdkPackage Remove="@(FrameworkSdkPackage)" Condition="'%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00.UWPDesktop' or '%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00.Debug.UWPDesktop'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
@@ -163,8 +161,6 @@
|
||||
<ItemGroup>
|
||||
<FrameworkSdkReference Remove="@(FrameworkSdkReference)" Condition="'%(FrameworkSdkReference.SimpleName)'=='Microsoft.VCLibs'" />
|
||||
<FrameworkSdkPackage Remove="@(FrameworkSdkPackage)" Condition="'%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00' or '%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00.Debug'" />
|
||||
<FrameworkSdkReference Remove="@(FrameworkSdkReference)" Condition="'%(FrameworkSdkReference.SimpleName)'=='Microsoft.VCLibs.Desktop'" />
|
||||
<FrameworkSdkPackage Remove="@(FrameworkSdkPackage)" Condition="'%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00.UWPDesktop' or '%(FrameworkSdkPackage.Name)'=='Microsoft.VCLibs.140.00.Debug.UWPDesktop'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
@@ -10,11 +10,9 @@
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
|
||||
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization">
|
||||
IgnorableNamespaces="uap mp rescap uap3">
|
||||
|
||||
<Identity
|
||||
Name="WindowsTerminalDev"
|
||||
@@ -25,14 +23,6 @@
|
||||
<DisplayName>ms-resource:AppStoreNameDev</DisplayName>
|
||||
<PublisherDisplayName>A Lone Developer</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
<!-- Older versions of Windows 10 respect this -->
|
||||
<desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>
|
||||
<!-- Newer versions of Windows 10 plus all versions of Windows 11 respect this -->
|
||||
<virtualization:RegistryWriteVirtualization>
|
||||
<virtualization:ExcludedKeys>
|
||||
<virtualization:ExcludedKey>HKEY_CURRENT_USER\Console\%%Startup</virtualization:ExcludedKey>
|
||||
</virtualization:ExcludedKeys>
|
||||
</virtualization:RegistryWriteVirtualization>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
@@ -146,6 +136,5 @@
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<rescap:Capability Name="unvirtualizedResources" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
|
||||
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
|
||||
IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization">
|
||||
IgnorableNamespaces="uap mp rescap uap3">
|
||||
|
||||
<Identity
|
||||
Name="Microsoft.WindowsTerminalPreview"
|
||||
@@ -26,14 +24,6 @@
|
||||
<DisplayName>ms-resource:AppStoreNamePre</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
<!-- Older versions of Windows 10 respect this -->
|
||||
<desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>
|
||||
<!-- Newer versions of Windows 10 plus all versions of Windows 11 respect this -->
|
||||
<virtualization:RegistryWriteVirtualization>
|
||||
<virtualization:ExcludedKeys>
|
||||
<virtualization:ExcludedKey>HKEY_CURRENT_USER\Console\%%Startup</virtualization:ExcludedKey>
|
||||
</virtualization:ExcludedKeys>
|
||||
</virtualization:RegistryWriteVirtualization>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
@@ -235,7 +225,6 @@
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<rescap:Capability Name="unvirtualizedResources" />
|
||||
</Capabilities>
|
||||
|
||||
<Extensions>
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
|
||||
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
|
||||
IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization">
|
||||
IgnorableNamespaces="uap mp rescap uap3">
|
||||
|
||||
<Identity
|
||||
Name="Microsoft.WindowsTerminal"
|
||||
@@ -26,14 +24,6 @@
|
||||
<DisplayName>ms-resource:AppStoreName</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Images\StoreLogo.png</Logo>
|
||||
<!-- Older versions of Windows 10 respect this -->
|
||||
<desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>
|
||||
<!-- Newer versions of Windows 10 plus all versions of Windows 11 respect this -->
|
||||
<virtualization:RegistryWriteVirtualization>
|
||||
<virtualization:ExcludedKeys>
|
||||
<virtualization:ExcludedKey>HKEY_CURRENT_USER\Console\%%Startup</virtualization:ExcludedKey>
|
||||
</virtualization:ExcludedKeys>
|
||||
</virtualization:RegistryWriteVirtualization>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
@@ -235,7 +225,6 @@
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<rescap:Capability Name="unvirtualizedResources" />
|
||||
</Capabilities>
|
||||
|
||||
<Extensions>
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(til::color(0xFF, 0xFF, 0xFF, 255), til::color{ scheme->CursorColor() });
|
||||
|
||||
std::array<COLORREF, COLOR_TABLE_SIZE> expectedCampbellTable;
|
||||
const auto campbellSpan = std::span{ expectedCampbellTable };
|
||||
const auto campbellSpan = gsl::make_span(expectedCampbellTable);
|
||||
Utils::InitializeColorTable(campbellSpan);
|
||||
|
||||
for (size_t i = 0; i < expectedCampbellTable.size(); i++)
|
||||
|
||||
@@ -20,7 +20,7 @@ class JsonTestClass
|
||||
public:
|
||||
static Json::Value VerifyParseSucceeded(const std::string_view& content)
|
||||
{
|
||||
static const std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder{}.newCharReader() };
|
||||
static const std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
|
||||
|
||||
Json::Value root;
|
||||
std::string errs;
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
|
||||
static std::string toString(const Json::Value& json)
|
||||
{
|
||||
static const std::unique_ptr<Json::StreamWriter> writer{ Json::StreamWriterBuilder{}.newStreamWriter() };
|
||||
static const std::unique_ptr<Json::StreamWriter> writer{ Json::StreamWriterBuilder::StreamWriterBuilder().newStreamWriter() };
|
||||
|
||||
std::stringstream s;
|
||||
writer->write(json, &s);
|
||||
|
||||
@@ -38,9 +38,6 @@ namespace SettingsModelLocalTests
|
||||
TEST_METHOD(LayerProfileProperties);
|
||||
TEST_METHOD(LayerProfileIcon);
|
||||
TEST_METHOD(LayerProfilesOnArray);
|
||||
TEST_METHOD(ProfileWithEnvVars);
|
||||
TEST_METHOD(ProfileWithEnvVarsSameNameDifferentCases);
|
||||
|
||||
TEST_METHOD(DuplicateProfileTest);
|
||||
TEST_METHOD(TestGenGuidsForProfiles);
|
||||
TEST_METHOD(TestCorrectOldDefaultShellPaths);
|
||||
@@ -352,50 +349,6 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_NOT_EQUAL(settings->AllProfiles().GetAt(0).Guid(), settings->AllProfiles().GetAt(1).Guid());
|
||||
}
|
||||
|
||||
void ProfileTests::ProfileWithEnvVars()
|
||||
{
|
||||
const std::string profileString{ R"({
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"environment": {
|
||||
"VAR_1": "value1",
|
||||
"VAR_2": "value2",
|
||||
"VAR_3": "%VAR_3%;value3"
|
||||
}
|
||||
})" };
|
||||
const auto profile = implementation::Profile::FromJson(VerifyParseSucceeded(profileString));
|
||||
std::vector<IEnvironmentVariableMap> envVarMaps{};
|
||||
envVarMaps.emplace_back(profile->EnvironmentVariables());
|
||||
for (auto& envMap : envVarMaps)
|
||||
{
|
||||
VERIFY_ARE_EQUAL(static_cast<uint32_t>(3), envMap.Size());
|
||||
VERIFY_ARE_EQUAL(L"value1", envMap.Lookup(L"VAR_1"));
|
||||
VERIFY_ARE_EQUAL(L"value2", envMap.Lookup(L"VAR_2"));
|
||||
VERIFY_ARE_EQUAL(L"%VAR_3%;value3", envMap.Lookup(L"VAR_3"));
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileTests::ProfileWithEnvVarsSameNameDifferentCases()
|
||||
{
|
||||
const std::string userSettings{ R"({
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"environment": {
|
||||
"FOO": "VALUE",
|
||||
"Foo": "Value"
|
||||
}
|
||||
}
|
||||
]
|
||||
})" };
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings);
|
||||
const auto warnings = settings->Warnings();
|
||||
VERIFY_ARE_EQUAL(static_cast<uint32_t>(2), warnings.Size());
|
||||
uint32_t index;
|
||||
VERIFY_IS_TRUE(warnings.IndexOf(SettingsLoadWarnings::InvalidProfileEnvironmentVariables, index));
|
||||
}
|
||||
|
||||
void ProfileTests::TestCorrectOldDefaultShellPaths()
|
||||
{
|
||||
static constexpr std::string_view inboxProfiles{ R"({
|
||||
|
||||
@@ -165,13 +165,7 @@ namespace SettingsModelLocalTests
|
||||
"historySize": 9001,
|
||||
|
||||
"closeOnExit": "graceful",
|
||||
"experimental.retroTerminalEffect": false,
|
||||
"environment":
|
||||
{
|
||||
"KEY_1": "VALUE_1",
|
||||
"KEY_2": "%KEY_1%",
|
||||
"KEY_3": "%PATH%"
|
||||
}
|
||||
"experimental.retroTerminalEffect": false
|
||||
})" };
|
||||
|
||||
static constexpr std::string_view smallProfileString{ R"(
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<OpenConsoleCppWinRTProject>true</OpenConsoleCppWinRTProject>
|
||||
<!-- TerminalCppWinrt is not set intentionally -->
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
@@ -74,6 +73,8 @@
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\jsoncpp\json;$(OpenConsoleDir)src\inc;$(OpenConsoleDir)src\inc\test;$(WinRT_IncludePath)\..\cppwinrt\winrt;"$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\Generated Files";%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<!-- Required for JsonUtils.h. -->
|
||||
<AdditionalOptions>%(AdditionalOptions) /Zc:twoPhase-</AdditionalOptions>
|
||||
<!-- Manually disable unreachable code warning, because jconcpp has a ton of that. -->
|
||||
<DisableSpecificWarnings>4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
@@ -98,4 +99,15 @@
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.tests.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- From Microsoft.UI.Xaml.targets -->
|
||||
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
|
||||
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- We actually can just straight up reference MUX here, it's fine -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -18,7 +18,7 @@ Author(s):
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#include <LibraryIncludes.h>
|
||||
#include "LibraryIncludes.h"
|
||||
// This is inexplicable, but for whatever reason, cppwinrt conflicts with the
|
||||
// SDK definition of this function, so the only fix is to undef it.
|
||||
// from WinBase.h
|
||||
@@ -28,9 +28,8 @@ Author(s):
|
||||
#endif
|
||||
|
||||
#include <wil/cppwinrt.h>
|
||||
#include <Unknwn.h>
|
||||
#include <unknwn.h>
|
||||
#include <hstring.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <WexTestClass.h>
|
||||
#include <json.h>
|
||||
|
||||
@@ -98,26 +98,6 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
}
|
||||
}
|
||||
void _logCommands(winrt::Windows::Foundation::Collections::IVector<Command> commands, const int indentation = 1)
|
||||
{
|
||||
if (indentation == 1)
|
||||
{
|
||||
Log::Comment((commands.Size() == 0) ? L"Commands:\n <none>" : L"Commands:");
|
||||
}
|
||||
for (const auto& cmd : commands)
|
||||
{
|
||||
Log::Comment(fmt::format(L"{0:>{1}}* {2}",
|
||||
L"",
|
||||
indentation,
|
||||
cmd.Name())
|
||||
.c_str());
|
||||
|
||||
if (cmd.HasNestedCommands())
|
||||
{
|
||||
_logCommandNames(cmd.NestedCommands(), indentation + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void SettingsTests::TestIterateCommands()
|
||||
@@ -184,15 +164,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(0);
|
||||
VERIFY_ARE_EQUAL(L"iterable command profile0", command.Name());
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -210,8 +189,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(1);
|
||||
VERIFY_ARE_EQUAL(L"iterable command profile1", command.Name());
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -229,8 +207,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(2);
|
||||
VERIFY_ARE_EQUAL(L"iterable command profile2", command.Name());
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -310,16 +287,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(0);
|
||||
VERIFY_ARE_EQUAL(L"Split pane, profile: profile0", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"Split pane, profile: profile0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -337,9 +312,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(1);
|
||||
VERIFY_ARE_EQUAL(L"Split pane, profile: profile1", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"Split pane, profile: profile1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -357,9 +330,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(2);
|
||||
VERIFY_ARE_EQUAL(L"Split pane, profile: profile2", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"Split pane, profile: profile2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -441,16 +412,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(0);
|
||||
VERIFY_ARE_EQUAL(L"iterable command profile0", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -468,9 +437,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(1);
|
||||
VERIFY_ARE_EQUAL(L"iterable command profile1\"", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile1\"");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -488,9 +455,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(2);
|
||||
VERIFY_ARE_EQUAL(L"iterable command profile2", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"iterable command profile2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -562,15 +527,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
|
||||
|
||||
auto rootCommand = expandedCommands.GetAt(0);
|
||||
auto rootCommand = expandedCommands.Lookup(L"Connect to ssh...");
|
||||
VERIFY_IS_NOT_NULL(rootCommand);
|
||||
VERIFY_ARE_EQUAL(L"Connect to ssh...", rootCommand.Name());
|
||||
auto rootActionAndArgs = rootCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(rootActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
|
||||
@@ -657,16 +621,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
|
||||
|
||||
auto grandparentCommand = expandedCommands.GetAt(0);
|
||||
auto grandparentCommand = expandedCommands.Lookup(L"grandparent");
|
||||
VERIFY_IS_NOT_NULL(grandparentCommand);
|
||||
VERIFY_ARE_EQUAL(L"grandparent", grandparentCommand.Name());
|
||||
|
||||
auto grandparentActionAndArgs = grandparentCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(grandparentActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, grandparentActionAndArgs.Action());
|
||||
@@ -782,22 +744,17 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
|
||||
|
||||
const std::vector<std::wstring> profileNames{ L"profile0", L"profile1", L"profile2" };
|
||||
for (auto i = 0u; i < profileNames.size(); i++)
|
||||
for (auto name : std::vector<std::wstring>({ L"profile0", L"profile1", L"profile2" }))
|
||||
{
|
||||
const auto& name{ profileNames[i] };
|
||||
winrt::hstring commandName{ profileNames[i] + L"..." };
|
||||
|
||||
auto command = expandedCommands.GetAt(i);
|
||||
VERIFY_ARE_EQUAL(commandName, command.Name());
|
||||
|
||||
winrt::hstring commandName{ name + L"..." };
|
||||
auto command = expandedCommands.Lookup(commandName);
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -923,16 +880,14 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
|
||||
|
||||
auto rootCommand = expandedCommands.GetAt(0);
|
||||
auto rootCommand = expandedCommands.Lookup(L"New Tab With Profile...");
|
||||
VERIFY_IS_NOT_NULL(rootCommand);
|
||||
VERIFY_ARE_EQUAL(L"New Tab With Profile...", rootCommand.Name());
|
||||
|
||||
auto rootActionAndArgs = rootCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(rootActionAndArgs);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action());
|
||||
@@ -1027,16 +982,13 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(1u, expandedCommands.Size());
|
||||
|
||||
auto rootCommand = expandedCommands.GetAt(0);
|
||||
VERIFY_IS_NOT_NULL(rootCommand);
|
||||
VERIFY_ARE_EQUAL(L"New Pane...", rootCommand.Name());
|
||||
|
||||
auto rootCommand = expandedCommands.Lookup(L"New Pane...");
|
||||
VERIFY_IS_NOT_NULL(rootCommand);
|
||||
auto rootActionAndArgs = rootCommand.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(rootActionAndArgs);
|
||||
@@ -1253,8 +1205,8 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"${scheme.name}", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() };
|
||||
_logCommands(expandedCommands);
|
||||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
|
||||
|
||||
@@ -1263,9 +1215,7 @@ namespace TerminalAppLocalTests
|
||||
// just easy tests to write.
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(0);
|
||||
VERIFY_ARE_EQUAL(L"iterable command Campbell", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"iterable command Campbell");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -1283,9 +1233,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(1);
|
||||
VERIFY_ARE_EQUAL(L"iterable command Campbell PowerShell", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"iterable command Campbell PowerShell");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
@@ -1303,9 +1251,7 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.GetAt(2);
|
||||
VERIFY_ARE_EQUAL(L"iterable command Vintage", command.Name());
|
||||
|
||||
auto command = expandedCommands.Lookup(L"iterable command Vintage");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalApp/TerminalPage.h"
|
||||
#include "../TerminalApp/TerminalWindow.h"
|
||||
#include "../TerminalApp/MinMaxCloseControl.h"
|
||||
#include "../TerminalApp/TabRowControl.h"
|
||||
#include "../TerminalApp/ShortcutActionDispatch.h"
|
||||
#include "../TerminalApp/TerminalTab.h"
|
||||
#include "../TerminalApp/CommandPalette.h"
|
||||
#include "../TerminalApp/ContentManager.h"
|
||||
#include "CppWinrtTailored.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
@@ -112,8 +110,6 @@ namespace TerminalAppLocalTests
|
||||
void _initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
|
||||
CascadiaSettings initialSettings);
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> _commonSetup();
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::WindowProperties> _windowProperties;
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::ContentManager> _contentManager;
|
||||
};
|
||||
|
||||
template<typename TFunction>
|
||||
@@ -198,14 +194,8 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> page{ nullptr };
|
||||
|
||||
_windowProperties = winrt::make_self<winrt::TerminalApp::implementation::WindowProperties>();
|
||||
winrt::TerminalApp::WindowProperties props = *_windowProperties;
|
||||
|
||||
_contentManager = winrt::make_self<winrt::TerminalApp::implementation::ContentManager>();
|
||||
winrt::TerminalApp::ContentManager contentManager = *_contentManager;
|
||||
|
||||
auto result = RunOnUIThread([&page, props, contentManager]() {
|
||||
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>(props, contentManager);
|
||||
auto result = RunOnUIThread([&page]() {
|
||||
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>();
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -249,13 +239,9 @@ namespace TerminalAppLocalTests
|
||||
// it's weird.
|
||||
winrt::TerminalApp::TerminalPage projectedPage{ nullptr };
|
||||
|
||||
_windowProperties = winrt::make_self<winrt::TerminalApp::implementation::WindowProperties>();
|
||||
winrt::TerminalApp::WindowProperties props = *_windowProperties;
|
||||
_contentManager = winrt::make_self<winrt::TerminalApp::implementation::ContentManager>();
|
||||
winrt::TerminalApp::ContentManager contentManager = *_contentManager;
|
||||
Log::Comment(NoThrowString().Format(L"Construct the TerminalPage"));
|
||||
auto result = RunOnUIThread([&projectedPage, &page, initialSettings, props, contentManager]() {
|
||||
projectedPage = winrt::TerminalApp::TerminalPage(props, contentManager);
|
||||
auto result = RunOnUIThread([&projectedPage, &page, initialSettings]() {
|
||||
projectedPage = winrt::TerminalApp::TerminalPage();
|
||||
page.copy_from(winrt::get_self<winrt::TerminalApp::implementation::TerminalPage>(projectedPage));
|
||||
page->_settings = initialSettings;
|
||||
});
|
||||
@@ -1102,7 +1088,7 @@ namespace TerminalAppLocalTests
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
page->CommandPalette().Visibility(Visibility::Collapsed);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
@@ -1123,7 +1109,7 @@ namespace TerminalAppLocalTests
|
||||
// If you don't do this, the palette will just stay open, and the
|
||||
// next time we call _HandleNextTab, we'll continue traversing the
|
||||
// MRU list, instead of just hoping one entry.
|
||||
page->LoadCommandPalette().Visibility(Visibility::Collapsed);
|
||||
page->CommandPalette().Visibility(Visibility::Collapsed);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
@@ -1239,7 +1225,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->LoadCommandPalette());
|
||||
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->CommandPalette());
|
||||
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode");
|
||||
// At this point, the contents of the command palette's _mruTabs list is
|
||||
@@ -1256,16 +1242,14 @@ namespace TerminalAppLocalTests
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto page = _commonSetup();
|
||||
page->RenameWindowRequested([&page, this](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) {
|
||||
page->RenameWindowRequested([&page](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) {
|
||||
// In the real terminal, this would bounce up to the monarch and
|
||||
// come back down. Instead, immediately call back and set the name.
|
||||
//
|
||||
// This replicates how TerminalWindow works
|
||||
_windowProperties->WindowName(args.ProposedName());
|
||||
page->WindowName(args.ProposedName());
|
||||
});
|
||||
|
||||
auto windowNameChanged = false;
|
||||
_windowProperties->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
|
||||
page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable {
|
||||
if (args.PropertyName() == L"WindowNameForDisplay")
|
||||
{
|
||||
windowNameChanged = true;
|
||||
@@ -1276,7 +1260,7 @@ namespace TerminalAppLocalTests
|
||||
page->_RequestWindowRename(winrt::hstring{ L"Foo" });
|
||||
});
|
||||
TestOnUIThread([&]() {
|
||||
VERIFY_ARE_EQUAL(L"Foo", page->WindowProperties().WindowName());
|
||||
VERIFY_ARE_EQUAL(L"Foo", page->_WindowName);
|
||||
VERIFY_IS_TRUE(windowNameChanged,
|
||||
L"The window name should have changed, and we should have raised a notification that WindowNameForDisplay changed");
|
||||
});
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<!-- TerminalCppWinrt is intentionally not set -->
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
@@ -86,4 +85,15 @@
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.tests.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- From Microsoft.UI.Xaml.targets -->
|
||||
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
|
||||
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- We actually can just straight up reference MUX here, it's fine -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
<UseWmXml>true</UseWmXml>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<OpenConsoleCppWinRTProject>true</OpenConsoleCppWinRTProject>
|
||||
<EnableHybridCRT>false</EnableHybridCRT> <!-- C++/CLI projects can't deal -->
|
||||
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
|
||||
<!--
|
||||
These two properties are very important!
|
||||
@@ -128,6 +125,8 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)\src\common.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)\src\common.nugetversions.targets" />
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ HRESULT HwndTerminal::Initialize()
|
||||
|
||||
_renderEngine = std::move(dxEngine);
|
||||
|
||||
_terminal->Create({ 80, 25 }, 9001, *_renderer);
|
||||
_terminal->Create({ 80, 25 }, 1000, *_renderer);
|
||||
_terminal->SetWriteInputCallback([=](std::wstring_view input) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "HwndTerminalAutomationPeer.hpp"
|
||||
#include "../../types/UiaTracing.h"
|
||||
#include <UIAutomationCoreApi.h>
|
||||
#include <delayimp.h>
|
||||
|
||||
#pragma warning(suppress : 4471) // We don't control UIAutomationClient
|
||||
#include <UIAutomationClient.h>
|
||||
@@ -14,15 +13,6 @@ using namespace Microsoft::Console::Types;
|
||||
|
||||
static constexpr wchar_t UNICODE_NEWLINE{ L'\n' };
|
||||
|
||||
static int CheckDelayedProcException(int exception) noexcept
|
||||
{
|
||||
if (exception == VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND))
|
||||
{
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - creates a copy of the provided text with all of the control characters removed
|
||||
// Arguments:
|
||||
@@ -133,12 +123,6 @@ void HwndTerminalAutomationPeer::SignalCursorChanged()
|
||||
|
||||
void HwndTerminalAutomationPeer::NotifyNewOutput(std::wstring_view newOutput)
|
||||
{
|
||||
if (_notificationsUnavailable) [[unlikely]]
|
||||
{
|
||||
// What if you tried to notify, but God said no
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to suppress any events (or event data)
|
||||
// that is just the keypress the user made
|
||||
auto sanitized{ Sanitize(newOutput) };
|
||||
@@ -171,19 +155,5 @@ void HwndTerminalAutomationPeer::NotifyNewOutput(std::wstring_view newOutput)
|
||||
|
||||
const auto sanitizedBstr = wil::make_bstr_nothrow(sanitized.c_str());
|
||||
static auto activityId = wil::make_bstr_nothrow(L"TerminalTextOutput");
|
||||
_tryNotify(sanitizedBstr.get(), activityId.get());
|
||||
}
|
||||
|
||||
// This needs to be a separate function because it is using SEH try/except, which
|
||||
// is incompatible with C++ unwinding
|
||||
void HwndTerminalAutomationPeer::_tryNotify(BSTR string, BSTR activity)
|
||||
{
|
||||
__try
|
||||
{
|
||||
LOG_IF_FAILED(UiaRaiseNotificationEvent(this, NotificationKind_ActionCompleted, NotificationProcessing_All, string, activity));
|
||||
}
|
||||
__except (CheckDelayedProcException(GetExceptionCode()))
|
||||
{
|
||||
_notificationsUnavailable = true;
|
||||
}
|
||||
LOG_IF_FAILED(UiaRaiseNotificationEvent(this, NotificationKind_ActionCompleted, NotificationProcessing_All, sanitizedBstr.get(), activityId.get()));
|
||||
}
|
||||
|
||||
@@ -38,7 +38,5 @@ public:
|
||||
void NotifyNewOutput(std::wstring_view newOutput) override;
|
||||
#pragma endregion
|
||||
private:
|
||||
void _tryNotify(BSTR string, BSTR activity);
|
||||
std::deque<wchar_t> _keyEvents;
|
||||
bool _notificationsUnavailable{};
|
||||
};
|
||||
|
||||
@@ -57,8 +57,7 @@
|
||||
instead of APISet forwarders for easier Windows 7 compatibility. -->
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<AdditionalDependencies>delayimp.lib;Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>uiautomationcore.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<AdditionalDependencies>Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -14,11 +14,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
|
||||
CommandlineArgs(const winrt::array_view<const winrt::hstring>& args,
|
||||
winrt::hstring currentDirectory,
|
||||
const uint32_t showWindowCommand) :
|
||||
winrt::hstring currentDirectory) :
|
||||
_args{ args.begin(), args.end() },
|
||||
_cwd{ currentDirectory },
|
||||
_ShowWindowCommand{ showWindowCommand }
|
||||
_cwd{ currentDirectory }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -27,8 +25,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void Commandline(const winrt::array_view<const winrt::hstring>& value);
|
||||
winrt::com_array<winrt::hstring> Commandline();
|
||||
|
||||
WINRT_PROPERTY(uint32_t, ShowWindowCommand, SW_NORMAL); // SW_NORMAL is 1, 0 is SW_HIDE
|
||||
|
||||
private:
|
||||
winrt::com_array<winrt::hstring> _args;
|
||||
winrt::hstring _cwd;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "ProposeCommandlineResult.h"
|
||||
|
||||
#include "Monarch.g.cpp"
|
||||
#include "WindowRequestedArgs.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt;
|
||||
@@ -302,10 +301,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (name == L"new")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t result = 0;
|
||||
|
||||
@@ -663,13 +658,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
if (targetWindow == WindowingBehaviorUseNone)
|
||||
{
|
||||
// In this case, the targetWindow was UseNone, which means that we
|
||||
// want to make a message box, but otherwise not make a Terminal
|
||||
// window.
|
||||
return winrt::make<Remoting::implementation::ProposeCommandlineResult>(false);
|
||||
}
|
||||
// If there's a valid ID returned, then let's try and find the peasant
|
||||
// that goes with it. Alternatively, if we were given a magic windowing
|
||||
// constant, we can use that to look up an appropriate peasant.
|
||||
@@ -699,11 +687,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
case WindowingBehaviorUseName:
|
||||
windowID = _lookupPeasantIdForName(targetWindowName);
|
||||
break;
|
||||
case WindowingBehaviorUseNone:
|
||||
// This should be impossible. The if statement above should have
|
||||
// prevented WindowingBehaviorUseNone from falling in here.
|
||||
// Explode, because this is a programming error.
|
||||
THROW_HR(E_UNEXPECTED);
|
||||
default:
|
||||
windowID = ::base::saturated_cast<uint64_t>(targetWindow);
|
||||
break;
|
||||
@@ -741,8 +724,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
result->WindowName(targetWindowName);
|
||||
result->ShouldCreateWindow(true);
|
||||
|
||||
_RequestNewWindowHandlers(*this, *winrt::make_self<WindowRequestedArgs>(*result, args));
|
||||
|
||||
// If this fails, it'll be logged in the following
|
||||
// TraceLoggingWrite statement, with succeeded=false
|
||||
}
|
||||
@@ -778,9 +759,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true) };
|
||||
result->Id(windowID);
|
||||
result->WindowName(targetWindowName);
|
||||
|
||||
_RequestNewWindowHandlers(*this, *winrt::make_self<WindowRequestedArgs>(*result, args));
|
||||
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
@@ -795,9 +773,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// In this case, no usable ID was provided. Return { true, nullopt }
|
||||
auto result = winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(true);
|
||||
result->WindowName(targetWindowName);
|
||||
|
||||
_RequestNewWindowHandlers(*this, *winrt::make_self<WindowRequestedArgs>(*result, args));
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
@@ -1059,96 +1034,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
return winrt::single_threaded_vector(std::move(vec));
|
||||
}
|
||||
|
||||
void Monarch::RequestMoveContent(winrt::hstring window,
|
||||
winrt::hstring content,
|
||||
uint32_t tabIndex,
|
||||
const Windows::Foundation::IReference<Windows::Foundation::Rect>& windowBounds)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_MoveContent_Requested",
|
||||
TraceLoggingWideString(window.c_str(), "window", "The name of the window we tried to move to"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
uint64_t windowId = _lookupPeasantIdForName(window);
|
||||
if (windowId == 0)
|
||||
{
|
||||
// Try the name as an integer ID
|
||||
uint32_t temp;
|
||||
if (!Utils::StringToUint(window.c_str(), temp))
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_MoveContent_FailedToParseId",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else
|
||||
{
|
||||
windowId = temp;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||
{
|
||||
auto request = winrt::make_self<implementation::AttachRequest>(content, tabIndex);
|
||||
targetPeasant.AttachContentToWindow(*request);
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_MoveContent_Completed",
|
||||
TraceLoggingInt64(windowId, "windowId", "The ID of the peasant which we sent the content to"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_MoveContent_NoWindow",
|
||||
TraceLoggingInt64(windowId, "windowId", "We could not find a peasant with this ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// In the case where window couldn't be found, then create a window
|
||||
// for that name / ID.
|
||||
//
|
||||
// Don't let the window literally be named "-1", because that's silly. Same with "new"
|
||||
const bool nameIsReserved = window == L"-1" || window == L"new";
|
||||
auto request = winrt::make_self<implementation::WindowRequestedArgs>(nameIsReserved ? L"" : window,
|
||||
content,
|
||||
windowBounds);
|
||||
_RequestNewWindowHandlers(*this, *request);
|
||||
}
|
||||
}
|
||||
|
||||
// Very similar to the above. Someone came and told us that they were the target of a drag/drop, and they know who started it.
|
||||
// We will go tell the person who started it that they should send that target the content which was dragged.
|
||||
void Monarch::RequestSendContent(const Remoting::RequestReceiveContentArgs& args)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SendContent_Requested",
|
||||
TraceLoggingUInt64(args.SourceWindow(), "source", "The window which started the drag"),
|
||||
TraceLoggingUInt64(args.TargetWindow(), "target", "The window which was the target of the drop"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
if (auto senderPeasant{ _getPeasant(args.SourceWindow()) })
|
||||
{
|
||||
senderPeasant.SendContent(args);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SendContent_Completed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else
|
||||
{
|
||||
// We couldn't find the peasant that started the drag. Well that
|
||||
// sure is weird, but that would indicate that the sender closed
|
||||
// after starting the drag. No matter. We can just do nothing.
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SendContent_NoWindow",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "Monarch.g.h"
|
||||
#include "Peasant.h"
|
||||
#include "WindowActivatedArgs.h"
|
||||
#include "WindowRequestedArgs.g.h"
|
||||
#include <atomic>
|
||||
|
||||
// We sure different GUIDs here depending on whether we're running a Release,
|
||||
@@ -39,38 +38,6 @@ namespace RemotingUnitTests
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct WindowRequestedArgs : public WindowRequestedArgsT<WindowRequestedArgs>
|
||||
{
|
||||
public:
|
||||
WindowRequestedArgs(const Remoting::ProposeCommandlineResult& windowInfo, const Remoting::CommandlineArgs& command) :
|
||||
_Id{ windowInfo.Id() ? windowInfo.Id().Value() : 0 }, // We'll use 0 as a sentinel, since no window will ever get to have that ID
|
||||
_WindowName{ windowInfo.WindowName() },
|
||||
_args{ command.Commandline() },
|
||||
_CurrentDirectory{ command.CurrentDirectory() },
|
||||
_ShowWindowCommand{ command.ShowWindowCommand() } {};
|
||||
|
||||
WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content, const Windows::Foundation::IReference<Windows::Foundation::Rect>& bounds) :
|
||||
_Id{ 0u },
|
||||
_WindowName{ window },
|
||||
_args{},
|
||||
_CurrentDirectory{},
|
||||
_Content{ content },
|
||||
_InitialBounds{ bounds } {};
|
||||
|
||||
void Commandline(const winrt::array_view<const winrt::hstring>& value) { _args = { value.begin(), value.end() }; };
|
||||
winrt::com_array<winrt::hstring> Commandline() { return winrt::com_array<winrt::hstring>{ _args.begin(), _args.end() }; }
|
||||
|
||||
WINRT_PROPERTY(uint64_t, Id);
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
WINRT_PROPERTY(winrt::hstring, CurrentDirectory);
|
||||
WINRT_PROPERTY(winrt::hstring, Content);
|
||||
WINRT_PROPERTY(uint32_t, ShowWindowCommand, SW_NORMAL);
|
||||
WINRT_PROPERTY(Windows::Foundation::IReference<Windows::Foundation::Rect>, InitialBounds);
|
||||
|
||||
private:
|
||||
winrt::com_array<winrt::hstring> _args;
|
||||
};
|
||||
|
||||
struct Monarch : public MonarchT<Monarch>
|
||||
{
|
||||
Monarch();
|
||||
@@ -93,9 +60,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
|
||||
|
||||
void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, const Windows::Foundation::IReference<Windows::Foundation::Rect>& windowBounds);
|
||||
void RequestSendContent(const Remoting::RequestReceiveContentArgs& args);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
@@ -103,8 +67,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs);
|
||||
|
||||
TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs);
|
||||
|
||||
private:
|
||||
uint64_t _ourPID;
|
||||
|
||||
@@ -229,5 +191,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(Monarch);
|
||||
BASIC_FACTORY(WindowRequestedArgs);
|
||||
}
|
||||
|
||||
@@ -18,20 +18,6 @@ namespace Microsoft.Terminal.Remoting
|
||||
Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass WindowRequestedArgs {
|
||||
WindowRequestedArgs(ProposeCommandlineResult windowInfo, CommandlineArgs command);
|
||||
|
||||
UInt64 Id { get; };
|
||||
String WindowName { get; };
|
||||
|
||||
String[] Commandline { get; };
|
||||
String CurrentDirectory { get; };
|
||||
UInt32 ShowWindowCommand { get; };
|
||||
|
||||
String Content { get; };
|
||||
Windows.Foundation.IReference<Windows.Foundation.Rect> InitialBounds { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass SummonWindowSelectionArgs {
|
||||
SummonWindowSelectionArgs();
|
||||
SummonWindowSelectionArgs(String windowName);
|
||||
@@ -45,7 +31,8 @@ namespace Microsoft.Terminal.Remoting
|
||||
Windows.Foundation.IReference<UInt64> WindowID;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass QuitAllRequestedArgs {
|
||||
[default_interface] runtimeclass QuitAllRequestedArgs
|
||||
{
|
||||
QuitAllRequestedArgs();
|
||||
Windows.Foundation.IAsyncAction BeforeQuitAllAction;
|
||||
}
|
||||
@@ -73,17 +60,12 @@ namespace Microsoft.Terminal.Remoting
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
|
||||
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
|
||||
|
||||
void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
|
||||
void RequestSendContent(RequestReceiveContentArgs args);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowRequestedArgs> RequestNewWindow;
|
||||
};
|
||||
|
||||
runtimeclass Monarch : [default] IMonarch
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include "GetWindowLayoutArgs.h"
|
||||
#include "Peasant.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "AttachRequest.g.cpp"
|
||||
#include "RequestReceiveContentArgs.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
@@ -277,22 +275,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::AttachContentToWindow(Remoting::AttachRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
_AttachRequestedHandlers(*this, request);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_AttachContentToWindow",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::Quit()
|
||||
{
|
||||
try
|
||||
@@ -328,9 +310,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
return args->WindowLayoutJson();
|
||||
}
|
||||
|
||||
void Peasant::SendContent(const Remoting::RequestReceiveContentArgs& args)
|
||||
{
|
||||
_SendContentRequestedHandlers(*this, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
#include "Peasant.g.h"
|
||||
#include "RenameRequestArgs.h"
|
||||
#include "AttachRequest.g.h"
|
||||
#include "RequestReceiveContentArgs.g.h"
|
||||
|
||||
namespace RemotingUnitTests
|
||||
{
|
||||
@@ -14,31 +12,6 @@ namespace RemotingUnitTests
|
||||
};
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct AttachRequest : public AttachRequestT<AttachRequest>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::hstring, Content);
|
||||
WINRT_PROPERTY(uint32_t, TabIndex);
|
||||
|
||||
public:
|
||||
AttachRequest(winrt::hstring content,
|
||||
uint32_t tabIndex) :
|
||||
_Content{ content },
|
||||
_TabIndex{ tabIndex } {};
|
||||
};
|
||||
|
||||
struct RequestReceiveContentArgs : RequestReceiveContentArgsT<RequestReceiveContentArgs>
|
||||
{
|
||||
WINRT_PROPERTY(uint64_t, SourceWindow);
|
||||
WINRT_PROPERTY(uint64_t, TargetWindow);
|
||||
WINRT_PROPERTY(uint32_t, TabIndex);
|
||||
|
||||
public:
|
||||
RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) :
|
||||
_SourceWindow{ src },
|
||||
_TargetWindow{ tgt },
|
||||
_TabIndex{ tabIndex } {};
|
||||
};
|
||||
|
||||
struct Peasant : public PeasantT<Peasant>
|
||||
{
|
||||
Peasant();
|
||||
@@ -59,14 +32,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
|
||||
void AttachContentToWindow(Remoting::AttachRequest request);
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
|
||||
|
||||
winrt::hstring GetWindowLayout();
|
||||
void SendContent(const winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs& args);
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
WINRT_PROPERTY(winrt::hstring, ActiveTabTitle);
|
||||
@@ -77,16 +47,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
|
||||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
|
||||
|
||||
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
|
||||
|
||||
TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest);
|
||||
TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs);
|
||||
|
||||
private:
|
||||
Peasant(const uint64_t testPID);
|
||||
uint64_t _ourPID;
|
||||
@@ -103,5 +69,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(Peasant);
|
||||
BASIC_FACTORY(RequestReceiveContentArgs);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,10 @@ namespace Microsoft.Terminal.Remoting
|
||||
runtimeclass CommandlineArgs
|
||||
{
|
||||
CommandlineArgs();
|
||||
CommandlineArgs(String[] args, String cwd, UInt32 showWindowCommand);
|
||||
CommandlineArgs(String[] args, String cwd);
|
||||
|
||||
String[] Commandline { get; set; };
|
||||
String CurrentDirectory { get; };
|
||||
UInt32 ShowWindowCommand { get; };
|
||||
String CurrentDirectory();
|
||||
};
|
||||
|
||||
runtimeclass RenameRequestArgs
|
||||
@@ -44,6 +43,7 @@ namespace Microsoft.Terminal.Remoting
|
||||
ToMouse,
|
||||
};
|
||||
|
||||
|
||||
[default_interface] runtimeclass SummonWindowBehavior {
|
||||
SummonWindowBehavior();
|
||||
Boolean MoveToCurrentDesktop;
|
||||
@@ -52,18 +52,6 @@ namespace Microsoft.Terminal.Remoting
|
||||
MonitorBehavior ToMonitor;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass AttachRequest {
|
||||
String Content { get; };
|
||||
UInt32 TabIndex { get; };
|
||||
}
|
||||
[default_interface] runtimeclass RequestReceiveContentArgs {
|
||||
RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex);
|
||||
|
||||
UInt64 SourceWindow { get; };
|
||||
UInt64 TargetWindow { get; };
|
||||
UInt32 TabIndex { get; };
|
||||
};
|
||||
|
||||
interface IPeasant
|
||||
{
|
||||
CommandlineArgs InitialArgs { get; };
|
||||
@@ -82,32 +70,23 @@ namespace Microsoft.Terminal.Remoting
|
||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
||||
void Summon(SummonWindowBehavior behavior);
|
||||
|
||||
void RequestShowNotificationIcon();
|
||||
void RequestHideNotificationIcon();
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
String GetWindowLayout();
|
||||
|
||||
void AttachContentToWindow(AttachRequest request);
|
||||
void SendContent(RequestReceiveContentArgs args);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, AttachRequest> AttachRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestReceiveContentArgs> SendContentRequested;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Peasant : IPeasant
|
||||
|
||||
@@ -2,59 +2,51 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "WindowManager.h"
|
||||
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
#include "MonarchFactory.h"
|
||||
|
||||
#include "CommandlineArgs.h"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
#include "FindTargetWindowArgs.h"
|
||||
#include "ProposeCommandlineResult.h"
|
||||
|
||||
#include "WindowManager.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
#include <WtExeUtils.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
namespace
|
||||
{
|
||||
const GUID& MonarchCLSID()
|
||||
{
|
||||
if (!IsPackaged()) [[unlikely]]
|
||||
{
|
||||
// Unpackaged installations don't have the luxury of magic package isolation
|
||||
// to stop them from accidentally touching each other's monarchs.
|
||||
// We need to enforce that ourselves by making their monarch CLSIDs unique
|
||||
// per install.
|
||||
// This applies in both portable mode and normal unpackaged mode.
|
||||
// We'll use a v5 UUID based on the install folder to unique them.
|
||||
static GUID processRootHashedGuid = []() {
|
||||
// {5456C4DB-557D-4A22-B043-B1577418E4AF}
|
||||
static constexpr GUID processRootHashedGuidBase = { 0x5456c4db, 0x557d, 0x4a22, { 0xb0, 0x43, 0xb1, 0x57, 0x74, 0x18, 0xe4, 0xaf } };
|
||||
|
||||
// Make a temporary monarch CLSID based on the unpackaged install root
|
||||
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
|
||||
modulePath.remove_filename();
|
||||
|
||||
return Utils::CreateV5Uuid(processRootHashedGuidBase, std::as_bytes(std::span{ modulePath.native() }));
|
||||
}();
|
||||
return processRootHashedGuid;
|
||||
}
|
||||
return Monarch_clsid;
|
||||
}
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
WindowManager::WindowManager()
|
||||
{
|
||||
_monarchWaitInterrupt.create();
|
||||
|
||||
// Register with COM as a server for the Monarch class
|
||||
_registerAsMonarch();
|
||||
// Instantiate an instance of the Monarch. This may or may not be in-proc!
|
||||
auto foundMonarch = false;
|
||||
while (!foundMonarch)
|
||||
{
|
||||
try
|
||||
{
|
||||
_createMonarchAndCallbacks();
|
||||
// _createMonarchAndCallbacks will initialize _isKing
|
||||
foundMonarch = true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If we fail to find the monarch,
|
||||
// stay in this jail until we do.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ExceptionInCtor",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowManager::~WindowManager()
|
||||
{
|
||||
// IMPORTANT! Tear down the registration as soon as we exit. If we're not a
|
||||
@@ -63,178 +55,32 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// monarch!
|
||||
CoRevokeClassObject(_registrationHostClass);
|
||||
_registrationHostClass = 0;
|
||||
}
|
||||
SignalClose();
|
||||
_monarchWaitInterrupt.SetEvent();
|
||||
|
||||
void WindowManager::_createMonarch()
|
||||
{
|
||||
// Heads up! This only works because we're using
|
||||
// "metadata-based-marshalling" for our WinRT types. That means the OS is
|
||||
// using the .winmd file we generate to figure out the proxy/stub
|
||||
// definitions for our types automatically. This only works in the following
|
||||
// cases:
|
||||
//
|
||||
// * If we're running unpackaged: the .winmd must be a sibling of the .exe
|
||||
// * If we're running packaged: the .winmd must be in the package root
|
||||
_monarch = try_create_instance<Remoting::IMonarch>(MonarchCLSID(),
|
||||
CLSCTX_LOCAL_SERVER);
|
||||
}
|
||||
|
||||
// Check if we became the king, and if we are, wire up callbacks.
|
||||
void WindowManager::_createCallbacks()
|
||||
{
|
||||
assert(_monarch);
|
||||
// Here, we're the king!
|
||||
//
|
||||
// This is where you should do any additional setup that might need to be
|
||||
// done when we become the king. This will be called both for the first
|
||||
// window, and when the current monarch dies.
|
||||
|
||||
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
|
||||
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
|
||||
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
|
||||
_monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers });
|
||||
|
||||
_monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow });
|
||||
}
|
||||
|
||||
void WindowManager::_registerAsMonarch()
|
||||
{
|
||||
winrt::check_hresult(CoRegisterClassObject(MonarchCLSID(),
|
||||
winrt::make<::MonarchFactory>().get(),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
REGCLS_MULTIPLEUSE,
|
||||
&_registrationHostClass));
|
||||
}
|
||||
|
||||
void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args)
|
||||
{
|
||||
_FindTargetWindowRequestedHandlers(sender, args);
|
||||
}
|
||||
void WindowManager::_raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args)
|
||||
{
|
||||
_RequestNewWindowHandlers(sender, args);
|
||||
}
|
||||
|
||||
Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args, const bool isolatedMode)
|
||||
{
|
||||
if (!isolatedMode)
|
||||
// A thread is joinable once it's been started. Basically this just
|
||||
// makes sure that the thread isn't just default-constructed.
|
||||
if (_electionThread.joinable())
|
||||
{
|
||||
// _createMonarch always attempts to connect an existing monarch. In
|
||||
// isolated mode, we don't want to do that.
|
||||
_createMonarch();
|
||||
_electionThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::SignalClose()
|
||||
{
|
||||
if (_monarch)
|
||||
{
|
||||
// We connected to a monarch instance, not us though. This won't hit
|
||||
// in isolated mode.
|
||||
|
||||
// Send the commandline over to the monarch process
|
||||
if (_proposeToMonarch(args))
|
||||
try
|
||||
{
|
||||
// If that succeeded, then we don't need to make a new window.
|
||||
// Our job is done. Either the monarch is going to run the
|
||||
// commandline in an existing window, or a new one, but either way,
|
||||
// this process doesn't need to make a new window.
|
||||
|
||||
return winrt::make<ProposeCommandlineResult>(false);
|
||||
}
|
||||
// Otherwise, we'll try to handle this ourselves.
|
||||
}
|
||||
|
||||
// Theoretically, this condition is always true here:
|
||||
//
|
||||
// if (_monarch == nullptr)
|
||||
//
|
||||
// If we do still have a _monarch at this point, then we must have
|
||||
// successfully proposed to it in _proposeToMonarch, so we can't get
|
||||
// here with a monarch.
|
||||
{
|
||||
// No preexisting instance.
|
||||
|
||||
// Raise an event, to ask how to handle this commandline. We can't ask
|
||||
// the app ourselves - we exist isolated from that knowledge (and
|
||||
// dependency hell). The WindowManager will raise this up to the app
|
||||
// host, which will then ask the AppLogic, who will then parse the
|
||||
// commandline and determine the provided ID of the window.
|
||||
auto findWindowArgs{ winrt::make_self<Remoting::implementation::FindTargetWindowArgs>(args) };
|
||||
|
||||
// This is handled by some handler in-proc
|
||||
_FindTargetWindowRequestedHandlers(*this, *findWindowArgs);
|
||||
|
||||
// After the event was handled, ResultTargetWindow() will be filled with
|
||||
// the parsed result.
|
||||
const auto targetWindow = findWindowArgs->ResultTargetWindow();
|
||||
const auto targetWindowName = findWindowArgs->ResultTargetWindowName();
|
||||
|
||||
if (targetWindow == WindowingBehaviorUseNone)
|
||||
{
|
||||
// This commandline doesn't deserve a window. Don't make a monarch
|
||||
// either.
|
||||
return winrt::make<ProposeCommandlineResult>(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This commandline _does_ want a window, which means we do want
|
||||
// to create a window, and a monarch.
|
||||
//
|
||||
// Congrats! This is now THE PROCESS. It's the only one that's
|
||||
// getting any windows.
|
||||
|
||||
// In isolated mode, we don't want to register as the monarch,
|
||||
// we just want to make a local one. So we'll skip this step.
|
||||
// The condition below it will handle making the unregistered
|
||||
// local monarch.
|
||||
|
||||
if (!isolatedMode)
|
||||
{
|
||||
_registerAsMonarch();
|
||||
_createMonarch();
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_IntentionallyIsolated",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
if (!_monarch)
|
||||
{
|
||||
// Something catastrophically bad happened here OR we were
|
||||
// intentionally in isolated mode. We don't want to just
|
||||
// exit immediately. Instead, we'll just instantiate a local
|
||||
// Monarch instance, without registering it. We're firmly in
|
||||
// the realm of undefined behavior, but better to have some
|
||||
// window than not.
|
||||
_monarch = winrt::make<Monarch>();
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_FailedToCoCreate",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
_createCallbacks();
|
||||
|
||||
// So, we wanted a new peasant. Cool!
|
||||
//
|
||||
// We need to fill in args.ResultTargetWindow,
|
||||
// args.ResultTargetWindowName so that we can create the new
|
||||
// window with those values. Otherwise, the very first window
|
||||
// won't obey the given name / ID.
|
||||
//
|
||||
// So let's just ask the monarch (ourselves) to get those values.
|
||||
return _monarch.ProposeCommandline(args);
|
||||
_monarch.SignalClose(_peasant.GetID());
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper attempting to call to the monarch multiple times. If the monarch
|
||||
// fails to respond, or we encounter any sort of error, we'll try again
|
||||
// until we find one, or decisively determine there isn't one.
|
||||
bool WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args)
|
||||
void WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args,
|
||||
std::optional<uint64_t>& givenID,
|
||||
winrt::hstring& givenName)
|
||||
{
|
||||
// these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below.
|
||||
static constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
|
||||
@@ -268,9 +114,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// dies between now and the inspection of
|
||||
// `result.ShouldCreateWindow` below, we don't want to explode
|
||||
// (since _proposeToMonarch is not try/caught).
|
||||
auto outOfProcResult = _monarch.ProposeCommandline(args);
|
||||
result = winrt::make<implementation::ProposeCommandlineResult>(outOfProcResult);
|
||||
|
||||
_monarch.ProposeCommandline(args);
|
||||
return true;
|
||||
proposedCommandline = true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -307,75 +154,560 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// Set the monarch to null, so that we'll create a new one
|
||||
// (or just generally check if we need to even make a window
|
||||
// for this commandline.)
|
||||
_monarch = nullptr;
|
||||
return false;
|
||||
_monarch = winrt::make<winrt::Microsoft::Terminal::Remoting::implementation::Monarch>();
|
||||
_createCallbacks();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We failed to ask the monarch. It must have died. Try and
|
||||
// find another monarch.
|
||||
_createMonarch();
|
||||
if (!_monarch)
|
||||
{
|
||||
// We failed to create a monarch. That means there
|
||||
// aren't any other windows, and we can become the monarch.
|
||||
return false;
|
||||
}
|
||||
// Go back around the loop.
|
||||
// find the real monarch. Don't perform an election, that
|
||||
// assumes we have a peasant, which we don't yet.
|
||||
_createMonarchAndCallbacks();
|
||||
// _createMonarchAndCallbacks will initialize _isKing
|
||||
}
|
||||
if (_isKing)
|
||||
{
|
||||
// We became the king. We don't need to ProposeCommandline to ourself, we're just
|
||||
// going to do it.
|
||||
//
|
||||
// Return early, because there's nothing else for us to do here.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_proposeToMonarch_tryAgain",
|
||||
"WindowManager_proposeToMonarch_becameKing",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// In WindowManager::ProposeCommandline, had we been the
|
||||
// king originally, we would have started by setting
|
||||
// this to true. We became the monarch here, so set it
|
||||
// here as well.
|
||||
_shouldCreateWindow = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Here, we created the new monarch, it wasn't us, so we're
|
||||
// gonna go through the while loop again and ask the new
|
||||
// king.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_proposeToMonarch_tryAgain",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
// Here, the monarch (not us) has replied to the message. Get the
|
||||
// valuables out of the response:
|
||||
_shouldCreateWindow = result.ShouldCreateWindow();
|
||||
if (result.Id())
|
||||
{
|
||||
givenID = result.Id().Value();
|
||||
}
|
||||
givenName = result.WindowName();
|
||||
|
||||
// TraceLogging doesn't have a good solution for logging an
|
||||
// optional. So we have to repeat the calls here:
|
||||
if (givenID)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingPointer(nullptr, "Id", "No ID provided"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args)
|
||||
{
|
||||
// If we're the king, we _definitely_ want to process the arguments, we were
|
||||
// launched with them!
|
||||
//
|
||||
// Otherwise, the King will tell us if we should make a new window
|
||||
_shouldCreateWindow = _isKing;
|
||||
std::optional<uint64_t> givenID;
|
||||
winrt::hstring givenName{};
|
||||
if (!_isKing)
|
||||
{
|
||||
_proposeToMonarch(args, givenID, givenName);
|
||||
}
|
||||
|
||||
// During _proposeToMonarch, it's possible that we found that the king was dead, and we're the new king. Cool! Do this now.
|
||||
if (_isKing)
|
||||
{
|
||||
// We're the monarch, we don't need to propose anything. We're just
|
||||
// going to do it.
|
||||
//
|
||||
// However, we _do_ need to ask what our name should be. It's
|
||||
// possible someone started the _first_ wt with something like `wt
|
||||
// -w king` as the commandline - we want to make sure we set our
|
||||
// name to "king".
|
||||
//
|
||||
// The FindTargetWindow event is the WindowManager's way of saying
|
||||
// "I do not know how to figure out how to turn this list of args
|
||||
// into a window ID/name. Whoever's listening to this event does, so
|
||||
// I'll ask them". It's a convoluted way of hooking the
|
||||
// WindowManager up to AppLogic without actually telling it anything
|
||||
// about TerminalApp (or even WindowsTerminal)
|
||||
auto findWindowArgs{ winrt::make_self<Remoting::implementation::FindTargetWindowArgs>(args) };
|
||||
_raiseFindTargetWindowRequested(nullptr, *findWindowArgs);
|
||||
|
||||
const auto responseId = findWindowArgs->ResultTargetWindow();
|
||||
if (responseId > 0)
|
||||
{
|
||||
givenID = ::base::saturated_cast<uint64_t>(responseId);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline_AsMonarch",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else if (responseId == WindowingBehaviorUseName)
|
||||
{
|
||||
givenName = findWindowArgs->ResultTargetWindowName();
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline_AsMonarch",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ProposeCommandline_AsMonarch",
|
||||
TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"),
|
||||
TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"),
|
||||
TraceLoggingWideString(L"", "Name", "The name we should assign this window"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
|
||||
if (_shouldCreateWindow)
|
||||
{
|
||||
// If we should create a new window, then instantiate our Peasant
|
||||
// instance, and tell that peasant to handle that commandline.
|
||||
_createOurPeasant({ givenID }, givenName);
|
||||
|
||||
// Spawn a thread to wait on the monarch, and handle the election
|
||||
if (!_isKing)
|
||||
{
|
||||
_createPeasantThread();
|
||||
}
|
||||
|
||||
// This right here will just tell us to stash the args away for the
|
||||
// future. The AppHost hasn't yet set up the callbacks, and the rest
|
||||
// of the app hasn't started at all. We'll note them and come back
|
||||
// later.
|
||||
_peasant.ExecuteCommandline(args);
|
||||
}
|
||||
// Otherwise, we'll do _nothing_.
|
||||
}
|
||||
|
||||
bool WindowManager::ShouldCreateWindow()
|
||||
{
|
||||
return _shouldCreateWindow;
|
||||
}
|
||||
|
||||
void WindowManager::_registerAsMonarch()
|
||||
{
|
||||
winrt::check_hresult(CoRegisterClassObject(Monarch_clsid,
|
||||
winrt::make<::MonarchFactory>().get(),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
REGCLS_MULTIPLEUSE,
|
||||
&_registrationHostClass));
|
||||
}
|
||||
|
||||
void WindowManager::_createMonarch()
|
||||
{
|
||||
// Heads up! This only works because we're using
|
||||
// "metadata-based-marshalling" for our WinRT types. That means the OS is
|
||||
// using the .winmd file we generate to figure out the proxy/stub
|
||||
// definitions for our types automatically. This only works in the following
|
||||
// cases:
|
||||
//
|
||||
// * If we're running unpackaged: the .winmd must be a sibling of the .exe
|
||||
// * If we're running packaged: the .winmd must be in the package root
|
||||
_monarch = create_instance<Remoting::IMonarch>(Monarch_clsid,
|
||||
CLSCTX_LOCAL_SERVER);
|
||||
}
|
||||
|
||||
// Tries to instantiate a monarch, tries again, and eventually either throws
|
||||
// (so that the caller will try again) or falls back to the isolated
|
||||
// monarch.
|
||||
void WindowManager::_redundantCreateMonarch()
|
||||
{
|
||||
_createMonarch();
|
||||
|
||||
if (_monarch == nullptr)
|
||||
{
|
||||
// See MSFT:38540483, GH#12774 for details.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_NullMonarchTryAgain",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
// Here we're gonna just give it a quick second try.Probably not
|
||||
// definitive, but might help.
|
||||
_createMonarch();
|
||||
}
|
||||
|
||||
if (_monarch == nullptr)
|
||||
{
|
||||
// See MSFT:38540483, GH#12774 for details.
|
||||
if constexpr (Feature_IsolatedMonarchMode::IsEnabled())
|
||||
{
|
||||
// Fall back to having a in proc monarch. Were now isolated from
|
||||
// other windows. This is a pretty torn state, but at least we
|
||||
// didn't just explode.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_NullMonarchIsolateMode",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
_monarch = winrt::make<winrt::Microsoft::Terminal::Remoting::implementation::Monarch>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The monarch is null. We're hoping that we can find another,
|
||||
// hopefully us. We're gonna go back around the loop again and
|
||||
// see what happens. If this is really an infinite loop (where
|
||||
// the OS won't even give us back US as the monarch), then I
|
||||
// suppose we'll find out soon enough.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_NullMonarchTryAgain",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
winrt::hresult_error(E_UNEXPECTED, L"Did not expect the Monarch to ever be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This can throw! Callers include:
|
||||
// - the constructor, who performs this in a loop until it successfully
|
||||
// find a a monarch
|
||||
// - the performElection method, which is called in the waitOnMonarch
|
||||
// thread. All the calls in that thread are wrapped in try/catch's
|
||||
// already.
|
||||
// - _createOurPeasant, who might do this in a loop to establish us with the
|
||||
// monarch.
|
||||
void WindowManager::_createMonarchAndCallbacks()
|
||||
{
|
||||
_redundantCreateMonarch();
|
||||
// We're pretty confident that we have a Monarch here.
|
||||
_createCallbacks();
|
||||
}
|
||||
|
||||
// Check if we became the king, and if we are, wire up callbacks.
|
||||
void WindowManager::_createCallbacks()
|
||||
{
|
||||
// Save the result of checking if we're the king. We want to avoid
|
||||
// unnecessary calls back and forth if we can.
|
||||
_isKing = _areWeTheKing();
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ConnectedToMonarch",
|
||||
TraceLoggingUInt64(_monarch.GetPID(), "monarchPID", "The PID of the new Monarch"),
|
||||
TraceLoggingBoolean(_isKing, "isKing", "true if we are the new monarch"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
if (_peasant)
|
||||
{
|
||||
if (const auto& lastActivated{ _peasant.GetLastActivatedArgs() })
|
||||
{
|
||||
// Inform the monarch of the time we were last activated
|
||||
_monarch.HandleActivatePeasant(lastActivated);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isKing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Here, we're the king!
|
||||
//
|
||||
// This is where you should do any additional setup that might need to be
|
||||
// done when we become the king. This will be called both for the first
|
||||
// window, and when the current monarch dies.
|
||||
|
||||
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
|
||||
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
|
||||
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
|
||||
_monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); });
|
||||
_monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
|
||||
_monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers });
|
||||
|
||||
_BecameMonarchHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
bool WindowManager::_areWeTheKing()
|
||||
{
|
||||
const auto ourPID{ GetCurrentProcessId() };
|
||||
const auto kingPID{ _monarch.GetPID() };
|
||||
return (ourPID == kingPID);
|
||||
}
|
||||
|
||||
Remoting::IPeasant WindowManager::_createOurPeasant(std::optional<uint64_t> givenID,
|
||||
const winrt::hstring& givenName)
|
||||
{
|
||||
auto p = winrt::make_self<Remoting::implementation::Peasant>();
|
||||
if (givenID)
|
||||
{
|
||||
p->AssignID(givenID.value());
|
||||
}
|
||||
|
||||
// If the name wasn't specified, this will be an empty string.
|
||||
p->WindowName(givenName);
|
||||
_peasant = *p;
|
||||
|
||||
// Try to add us to the monarch. If that fails, try to find a monarch
|
||||
// again, until we find one (we will eventually find us)
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
_monarch.AddPeasant(_peasant);
|
||||
break;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Wrap this in its own try/catch, because this can throw.
|
||||
_createMonarchAndCallbacks();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// I don't think we can ever get here, but the compiler doesn't know
|
||||
return false;
|
||||
}
|
||||
|
||||
Remoting::Peasant WindowManager::CreatePeasant(const Remoting::WindowRequestedArgs& args)
|
||||
{
|
||||
auto p = winrt::make_self<Remoting::implementation::Peasant>();
|
||||
// This will be false if the Id is 0, which is our sentinel for "no specific ID was requested"
|
||||
if (const auto id = args.Id())
|
||||
{
|
||||
p->AssignID(id);
|
||||
}
|
||||
|
||||
// If the name wasn't specified, this will be an empty string.
|
||||
p->WindowName(args.WindowName());
|
||||
|
||||
p->ExecuteCommandline(*winrt::make_self<CommandlineArgs>(args.Commandline(), args.CurrentDirectory(), args.ShowWindowCommand()));
|
||||
|
||||
_monarch.AddPeasant(*p);
|
||||
|
||||
p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers });
|
||||
_peasant.GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers });
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_CreateOurPeasant",
|
||||
TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"),
|
||||
TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
return *p;
|
||||
// If the peasant asks us to quit we should not try to act in future elections.
|
||||
_peasant.QuitRequested([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto wm = weakThis.get())
|
||||
{
|
||||
wm->_monarchWaitInterrupt.SetEvent();
|
||||
}
|
||||
});
|
||||
|
||||
return _peasant;
|
||||
}
|
||||
|
||||
void WindowManager::SignalClose(const Remoting::Peasant& peasant)
|
||||
// Method Description:
|
||||
// - Attempt to connect to the monarch process. This might be us!
|
||||
// - For the new monarch, add us to their list of peasants.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - true iff we're the new monarch process.
|
||||
// NOTE: This can throw!
|
||||
bool WindowManager::_performElection()
|
||||
{
|
||||
if (_monarch)
|
||||
_createMonarchAndCallbacks();
|
||||
|
||||
// Tell the new monarch who we are. We might be that monarch!
|
||||
_monarch.AddPeasant(_peasant);
|
||||
|
||||
// This method is only called when a _new_ monarch is elected. So
|
||||
// don't do anything here that needs to be done for all monarch
|
||||
// windows. This should only be for work that's done when a window
|
||||
// _becomes_ a monarch, after the death of the previous monarch.
|
||||
return _isKing;
|
||||
}
|
||||
|
||||
void WindowManager::_createPeasantThread()
|
||||
{
|
||||
// If we catch an exception trying to get at the monarch ever, we can
|
||||
// set the _monarchWaitInterrupt, and use that to trigger a new
|
||||
// election. Though, we wouldn't be able to retry the function that
|
||||
// caused the exception in the first place...
|
||||
|
||||
_electionThread = std::thread([this] {
|
||||
_waitOnMonarchThread();
|
||||
});
|
||||
}
|
||||
|
||||
void WindowManager::_waitOnMonarchThread()
|
||||
{
|
||||
// This is the array of HANDLEs that we're going to wait on in
|
||||
// WaitForMultipleObjects below.
|
||||
// * waits[0] will be the handle to the monarch process. It gets
|
||||
// signalled when the process exits / dies.
|
||||
// * waits[1] is the handle to our _monarchWaitInterrupt event. Another
|
||||
// thread can use that to manually break this loop. We'll do that when
|
||||
// we're getting torn down.
|
||||
HANDLE waits[2];
|
||||
waits[1] = _monarchWaitInterrupt.get();
|
||||
const auto peasantID = _peasant.GetID(); // safe: _peasant is in-proc.
|
||||
|
||||
auto exitThreadRequested = false;
|
||||
while (!exitThreadRequested)
|
||||
{
|
||||
// At any point in all this, the current monarch might die. If it
|
||||
// does, we'll go straight to a new election, in the "jail"
|
||||
// try/catch below. Worst case, eventually, we'll become the new
|
||||
// monarch.
|
||||
try
|
||||
{
|
||||
_monarch.SignalClose(peasant.GetID());
|
||||
// This might fail to even ask the monarch for its PID.
|
||||
wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS,
|
||||
FALSE,
|
||||
static_cast<DWORD>(_monarch.GetPID())) };
|
||||
|
||||
// If we fail to open the monarch, then they don't exist
|
||||
// anymore! Go straight to an election.
|
||||
if (hMonarch.get() == nullptr)
|
||||
{
|
||||
const auto gle = GetLastError();
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_FailedToOpenMonarch",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
|
||||
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
exitThreadRequested = _performElection();
|
||||
continue;
|
||||
}
|
||||
|
||||
waits[0] = hMonarch.get();
|
||||
auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE);
|
||||
|
||||
switch (waitResult)
|
||||
{
|
||||
case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the monarch process
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_MonarchDied",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
// Connect to the new monarch, which might be us!
|
||||
// If we become the monarch, then we'll return true and exit this thread.
|
||||
exitThreadRequested = _performElection();
|
||||
break;
|
||||
|
||||
case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_MonarchWaitInterrupted",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
exitThreadRequested = true;
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
// This should be impossible.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_MonarchWaitTimeout",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
exitThreadRequested = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// Returning any other value is invalid. Just die.
|
||||
const auto gle = GetLastError();
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_WaitFailed",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
|
||||
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
ExitProcess(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Theoretically, if window[1] dies when we're trying to get
|
||||
// its PID we'll get here. If we just try to do the election
|
||||
// once here, it's possible we might elect window[2], but have
|
||||
// it die before we add ourselves as a peasant. That
|
||||
// _performElection call will throw, and we wouldn't catch it
|
||||
// here, and we'd die.
|
||||
|
||||
// Instead, we're going to have a resilient election process.
|
||||
// We're going to keep trying an election, until one _doesn't_
|
||||
// throw an exception. That might mean burning through all the
|
||||
// other dying monarchs until we find us as the monarch. But if
|
||||
// this process is alive, then there's _someone_ in the line of
|
||||
// succession.
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ExceptionInWaitThread",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
auto foundNewMonarch = false;
|
||||
while (!foundNewMonarch)
|
||||
{
|
||||
try
|
||||
{
|
||||
exitThreadRequested = _performElection();
|
||||
// It doesn't matter if we're the monarch, or someone
|
||||
// else is, but if we complete the election, then we've
|
||||
// registered with a new one. We can escape this jail
|
||||
// and re-enter society.
|
||||
foundNewMonarch = true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// If we fail to acknowledge the results of the election,
|
||||
// stay in this jail until we do.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_ExceptionInNestedWaitThread",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
}
|
||||
|
||||
Remoting::Peasant WindowManager::CurrentWindow()
|
||||
{
|
||||
return _peasant;
|
||||
}
|
||||
|
||||
void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args)
|
||||
{
|
||||
_FindTargetWindowRequestedHandlers(sender, args);
|
||||
}
|
||||
|
||||
bool WindowManager::IsMonarch()
|
||||
{
|
||||
return _isKing;
|
||||
}
|
||||
|
||||
void WindowManager::SummonWindow(const Remoting::SummonWindowSelectionArgs& args)
|
||||
{
|
||||
// We should only ever get called when we are the monarch, because only
|
||||
@@ -409,16 +741,42 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to show a notification icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestShowNotificationIcon()
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestShowNotificationIcon();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to hide its notification icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestHideNotificationIcon()
|
||||
{
|
||||
auto strongThis{ get_strong() };
|
||||
co_await winrt::resume_background();
|
||||
_peasant.RequestHideNotificationIcon();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to quit all windows.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget WindowManager::RequestQuitAll(Remoting::Peasant peasant)
|
||||
winrt::fire_and_forget WindowManager::RequestQuitAll()
|
||||
{
|
||||
auto strongThis{ get_strong() };
|
||||
co_await winrt::resume_background();
|
||||
peasant.RequestQuitAll();
|
||||
_peasant.RequestQuitAll();
|
||||
}
|
||||
|
||||
bool WindowManager::DoesQuakeWindowExist()
|
||||
@@ -426,9 +784,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
return _monarch.DoesQuakeWindowExist();
|
||||
}
|
||||
|
||||
void WindowManager::UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant)
|
||||
void WindowManager::UpdateActiveTabTitle(winrt::hstring title)
|
||||
{
|
||||
winrt::get_self<implementation::Peasant>(peasant)->ActiveTabTitle(title);
|
||||
winrt::get_self<implementation::Peasant>(_peasant)->ActiveTabTitle(title);
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> WindowManager::GetAllWindowLayouts()
|
||||
@@ -443,19 +801,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window,
|
||||
winrt::hstring content,
|
||||
uint32_t tabIndex,
|
||||
Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds)
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_monarch.RequestMoveContent(window, content, tabIndex, windowBounds);
|
||||
}
|
||||
|
||||
winrt::fire_and_forget WindowManager::RequestSendContent(Remoting::RequestReceiveContentArgs args)
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_monarch.RequestSendContent(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Class Name:
|
||||
- WindowManager.h
|
||||
|
||||
Abstract:
|
||||
- The Window Manager takes care of coordinating the monarch and peasant for this
|
||||
process.
|
||||
@@ -14,7 +16,9 @@ Abstract:
|
||||
- When the monarch needs to ask the TerminalApp about how to parse a
|
||||
commandline, it'll ask by raising an event that we'll bubble up to the
|
||||
AppHost.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WindowManager.g.h"
|
||||
@@ -25,51 +29,65 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct WindowManager : public WindowManagerT<WindowManager>
|
||||
{
|
||||
public:
|
||||
WindowManager();
|
||||
~WindowManager();
|
||||
winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args, const bool isolatedMode);
|
||||
Remoting::Peasant CreatePeasant(const Remoting::WindowRequestedArgs& args);
|
||||
|
||||
void SignalClose(const Remoting::Peasant& peasant);
|
||||
void ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
|
||||
bool ShouldCreateWindow();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow();
|
||||
bool IsMonarch();
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
void SignalClose();
|
||||
|
||||
void SummonAllWindows();
|
||||
uint64_t GetNumberOfPeasants();
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
|
||||
uint64_t GetNumberOfPeasants();
|
||||
|
||||
static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant);
|
||||
void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant);
|
||||
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
|
||||
winrt::fire_and_forget RequestShowNotificationIcon();
|
||||
winrt::fire_and_forget RequestHideNotificationIcon();
|
||||
winrt::fire_and_forget RequestQuitAll();
|
||||
bool DoesQuakeWindowExist();
|
||||
|
||||
winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds);
|
||||
winrt::fire_and_forget RequestSendContent(Remoting::RequestReceiveContentArgs args);
|
||||
void UpdateActiveTabTitle(winrt::hstring title);
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
|
||||
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs);
|
||||
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
|
||||
|
||||
TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs);
|
||||
|
||||
private:
|
||||
bool _shouldCreateWindow{ false };
|
||||
bool _isKing{ false };
|
||||
DWORD _registrationHostClass{ 0 };
|
||||
winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr };
|
||||
winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr };
|
||||
|
||||
wil::unique_event _monarchWaitInterrupt;
|
||||
std::thread _electionThread;
|
||||
|
||||
void _createMonarch();
|
||||
void _registerAsMonarch();
|
||||
|
||||
bool _proposeToMonarch(const Remoting::CommandlineArgs& args);
|
||||
|
||||
void _createMonarch();
|
||||
void _redundantCreateMonarch();
|
||||
void _createMonarchAndCallbacks();
|
||||
void _createCallbacks();
|
||||
bool _areWeTheKing();
|
||||
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional<uint64_t> givenID,
|
||||
const winrt::hstring& givenName);
|
||||
|
||||
bool _performElection();
|
||||
void _createPeasantThread();
|
||||
void _waitOnMonarchThread();
|
||||
void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args);
|
||||
void _raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
|
||||
|
||||
void _proposeToMonarch(const Remoting::CommandlineArgs& args,
|
||||
std::optional<uint64_t>& givenID,
|
||||
winrt::hstring& givenName);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,36 +7,29 @@ namespace Microsoft.Terminal.Remoting
|
||||
[default_interface] runtimeclass WindowManager
|
||||
{
|
||||
WindowManager();
|
||||
|
||||
ProposeCommandlineResult ProposeCommandline(CommandlineArgs args, Boolean isolatedMode);
|
||||
Peasant CreatePeasant(WindowRequestedArgs args);
|
||||
|
||||
void SignalClose(Peasant p);
|
||||
|
||||
void UpdateActiveTabTitle(String title, Peasant p);
|
||||
static void RequestQuitAll(Peasant p);
|
||||
|
||||
void ProposeCommandline(CommandlineArgs args);
|
||||
void SignalClose();
|
||||
Boolean ShouldCreateWindow { get; };
|
||||
IPeasant CurrentWindow();
|
||||
Boolean IsMonarch { get; };
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
void SummonAllWindows();
|
||||
|
||||
void RequestShowNotificationIcon();
|
||||
void RequestHideNotificationIcon();
|
||||
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
|
||||
|
||||
UInt64 GetNumberOfPeasants();
|
||||
|
||||
void RequestQuitAll();
|
||||
void UpdateActiveTabTitle(String title);
|
||||
Boolean DoesQuakeWindowExist();
|
||||
|
||||
void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
|
||||
void RequestSendContent(RequestReceiveContentArgs args);
|
||||
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowRequestedArgs> RequestNewWindow;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
|
||||
IBindCtx* /*pBindContext*/)
|
||||
try
|
||||
{
|
||||
const auto runElevated = IsControlAndShiftPressed();
|
||||
|
||||
wil::com_ptr_nothrow<IShellItem> psi;
|
||||
RETURN_IF_FAILED(GetBestLocationFromSelectionOrSite(psiItemArray, psi.put()));
|
||||
if (!psi)
|
||||
@@ -44,22 +42,10 @@ try
|
||||
STARTUPINFOEX siEx{ 0 };
|
||||
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
|
||||
|
||||
// Explicitly create the terminal window visible.
|
||||
siEx.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
|
||||
siEx.StartupInfo.wShowWindow = SW_SHOWNORMAL;
|
||||
|
||||
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
|
||||
std::wstring cmdline;
|
||||
if (runElevated)
|
||||
{
|
||||
RETURN_IF_FAILED(wil::str_printf_nothrow(cmdline, LR"-(-d %s)-", QuoteAndEscapeCommandlineArg(pszName.get()).c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(wil::str_printf_nothrow(cmdline, LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()));
|
||||
}
|
||||
RETURN_IF_FAILED(wil::str_printf_nothrow(cmdline, LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()));
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
|
||||
runElevated ? modulePath.replace_filename(ElevateShimExe).c_str() : nullptr, // if elevation requested pass the elevate-shim.exe as the application name
|
||||
nullptr, // lpApplicationName
|
||||
cmdline.data(),
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
@@ -119,8 +105,7 @@ HRESULT OpenTerminalHere::GetState(IShellItemArray* psiItemArray,
|
||||
|
||||
SFGAOF attributes;
|
||||
const bool isFileSystemItem = psi && (psi->GetAttributes(SFGAO_FILESYSTEM, &attributes) == S_OK);
|
||||
const bool isCompressed = psi && (psi->GetAttributes(SFGAO_FOLDER | SFGAO_STREAM, &attributes) == S_OK);
|
||||
*pCmdState = isFileSystemItem && !isCompressed ? ECS_ENABLED : ECS_HIDDEN;
|
||||
*pCmdState = isFileSystemItem ? ECS_ENABLED : ECS_HIDDEN;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -208,15 +193,3 @@ HRESULT OpenTerminalHere::GetBestLocationFromSelectionOrSite(IShellItemArray* ps
|
||||
RETURN_IF_FAILED(psi.copy_to(location));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Check is both ctrl and shift keys are pressed during activation of the shell extension
|
||||
bool OpenTerminalHere::IsControlAndShiftPressed()
|
||||
{
|
||||
short control = 0;
|
||||
short shift = 0;
|
||||
control = GetAsyncKeyState(VK_CONTROL);
|
||||
shift = GetAsyncKeyState(VK_SHIFT);
|
||||
|
||||
// GetAsyncKeyState returns a value with the most significant bit set to 1 if the key is pressed. This is the sign bit.
|
||||
return control < 0 && shift < 0;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ struct
|
||||
private:
|
||||
HRESULT GetLocationFromSite(IShellItem** location) const noexcept;
|
||||
HRESULT GetBestLocationFromSelectionOrSite(IShellItemArray* psiArray, IShellItem** location) const noexcept;
|
||||
bool IsControlAndShiftPressed();
|
||||
|
||||
wil::com_ptr_nothrow<IUnknown> site_;
|
||||
};
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AboutDialog.h"
|
||||
#include "AboutDialog.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include <WtExeUtils.h>
|
||||
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace ::TerminalApp;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
using IInspectable = Windows::Foundation::IInspectable;
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
AboutDialog::AboutDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
_queueUpdateCheck();
|
||||
}
|
||||
|
||||
winrt::hstring AboutDialog::ApplicationDisplayName()
|
||||
{
|
||||
return CascadiaSettings::ApplicationDisplayName();
|
||||
}
|
||||
|
||||
winrt::hstring AboutDialog::ApplicationVersion()
|
||||
{
|
||||
return CascadiaSettings::ApplicationVersion();
|
||||
}
|
||||
|
||||
void AboutDialog::_SendFeedbackOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& /*eventArgs*/)
|
||||
{
|
||||
#if defined(WT_BRANDING_RELEASE)
|
||||
ShellExecute(nullptr, nullptr, L"https://go.microsoft.com/fwlink/?linkid=2125419", nullptr, nullptr, SW_SHOW);
|
||||
#else
|
||||
ShellExecute(nullptr, nullptr, L"https://go.microsoft.com/fwlink/?linkid=2204904", nullptr, nullptr, SW_SHOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AboutDialog::_ThirdPartyNoticesOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
std::filesystem::path currentPath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
|
||||
currentPath.replace_filename(L"NOTICE.html");
|
||||
ShellExecute(nullptr, nullptr, currentPath.c_str(), nullptr, nullptr, SW_SHOW);
|
||||
}
|
||||
|
||||
winrt::fire_and_forget AboutDialog::_queueUpdateCheck()
|
||||
{
|
||||
auto strongThis = get_strong();
|
||||
auto now{ std::chrono::system_clock::now() };
|
||||
if (now - _lastUpdateCheck < std::chrono::days{ 1 })
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
_lastUpdateCheck = now;
|
||||
|
||||
if (!IsPackaged())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
co_await wil::resume_foreground(strongThis->Dispatcher());
|
||||
UpdatesAvailable(false);
|
||||
CheckingForUpdates(true);
|
||||
|
||||
try
|
||||
{
|
||||
#ifdef WT_BRANDING_DEV
|
||||
// **DEV BRANDING**: Always sleep for three seconds and then report that
|
||||
// there is an update available. This lets us test the system.
|
||||
co_await winrt::resume_after(std::chrono::seconds{ 3 });
|
||||
co_await wil::resume_foreground(strongThis->Dispatcher());
|
||||
UpdatesAvailable(true);
|
||||
#else // release build, likely has a store context
|
||||
if (auto storeContext{ winrt::Windows::Services::Store::StoreContext::GetDefault() })
|
||||
{
|
||||
const auto updates = co_await storeContext.GetAppAndOptionalStorePackageUpdatesAsync();
|
||||
co_await wil::resume_foreground(strongThis->Dispatcher());
|
||||
const auto numUpdates = updates.Size();
|
||||
if (numUpdates > 0)
|
||||
{
|
||||
UpdatesAvailable(true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// do nothing on failure
|
||||
}
|
||||
|
||||
co_await wil::resume_foreground(strongThis->Dispatcher());
|
||||
CheckingForUpdates(false);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AboutDialog.g.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct AboutDialog : AboutDialogT<AboutDialog>
|
||||
{
|
||||
public:
|
||||
AboutDialog();
|
||||
|
||||
winrt::hstring ApplicationDisplayName();
|
||||
winrt::hstring ApplicationVersion();
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, UpdatesAvailable, _PropertyChangedHandlers, false);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, CheckingForUpdates, _PropertyChangedHandlers, false);
|
||||
|
||||
private:
|
||||
friend struct AboutDialogT<AboutDialog>; // for Xaml to bind events
|
||||
|
||||
std::chrono::system_clock::time_point _lastUpdateCheck{};
|
||||
|
||||
void _ThirdPartyNoticesOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _SendFeedbackOnClick(const IInspectable& sender, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& eventArgs);
|
||||
winrt::fire_and_forget _queueUpdateCheck();
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(AboutDialog);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass AboutDialog : Windows.UI.Xaml.Controls.ContentDialog, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
AboutDialog();
|
||||
|
||||
String ApplicationDisplayName { get; };
|
||||
String ApplicationVersion { get; };
|
||||
|
||||
Boolean CheckingForUpdates { get; };
|
||||
Boolean UpdatesAvailable { get; };
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<ContentDialog x:Class="TerminalApp.AboutDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
||||
x:Uid="AboutDialog"
|
||||
DefaultButton="Close"
|
||||
PrimaryButtonClick="_SendFeedbackOnClick"
|
||||
Style="{StaticResource DefaultContentDialogStyle}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock IsTextSelectionEnabled="True">
|
||||
<Run AutomationProperties.HeadingLevel="1"
|
||||
Text="{x:Bind ApplicationDisplayName}" /> <LineBreak />
|
||||
<Run x:Uid="AboutDialog_VersionLabel" />
|
||||
<Run Text="{x:Bind ApplicationVersion}" />
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Padding="0,4,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Visibility="{x:Bind CheckingForUpdates, Mode=OneWay}">
|
||||
<mux:ProgressRing Width="16"
|
||||
Height="16"
|
||||
IsActive="True"
|
||||
IsIndeterminate="True" />
|
||||
<TextBlock x:Uid="AboutDialog_CheckingForUpdatesLabel"
|
||||
Padding="4,0,0,0" />
|
||||
</StackPanel>
|
||||
<StackPanel Padding="0,4,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Vertical"
|
||||
Visibility="{x:Bind UpdatesAvailable, Mode=OneWay}">
|
||||
<TextBlock IsTextSelectionEnabled="False">
|
||||
<Run x:Uid="AboutDialog_UpdateAvailableLabel" />
|
||||
</TextBlock>
|
||||
<!-- <Button x:Uid="AboutDialog_InstallUpdateButton"
|
||||
Margin="0" />-->
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<HyperlinkButton x:Uid="AboutDialog_SourceCodeLink"
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2203152" />
|
||||
<HyperlinkButton x:Uid="AboutDialog_DocumentationLink"
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125416" />
|
||||
<HyperlinkButton x:Uid="AboutDialog_ReleaseNotesLink"
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125417" />
|
||||
<HyperlinkButton x:Uid="AboutDialog_PrivacyPolicyLink"
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125418" />
|
||||
<HyperlinkButton x:Uid="AboutDialog_ThirdPartyNoticesLink"
|
||||
Click="_ThirdPartyNoticesOnClick" />
|
||||
</StackPanel>
|
||||
</ContentDialog>
|
||||
|
||||
@@ -24,5 +24,27 @@ namespace winrt::TerminalApp::implementation
|
||||
Name(command.Name());
|
||||
KeyChordText(command.KeyChordText());
|
||||
Icon(command.IconPath());
|
||||
|
||||
_commandChangedRevoker = command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) {
|
||||
auto item{ weakThis.get() };
|
||||
auto senderCommand{ sender.try_as<Microsoft::Terminal::Settings::Model::Command>() };
|
||||
|
||||
if (item && senderCommand)
|
||||
{
|
||||
auto changedProperty = e.PropertyName();
|
||||
if (changedProperty == L"Name")
|
||||
{
|
||||
item->Name(senderCommand.Name());
|
||||
}
|
||||
else if (changedProperty == L"KeyChordText")
|
||||
{
|
||||
item->KeyChordText(senderCommand.KeyChordText());
|
||||
}
|
||||
else if (changedProperty == L"IconPath")
|
||||
{
|
||||
item->Icon(senderCommand.IconPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user