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
|
||||
|
||||
1
.github/actions/spelling/allow/allow.txt
vendored
1
.github/actions/spelling/allow/allow.txt
vendored
@@ -100,7 +100,6 @@ TLDR
|
||||
tokenizes
|
||||
tonos
|
||||
toolset
|
||||
truthiness
|
||||
tshe
|
||||
ubuntu
|
||||
uiatextrange
|
||||
|
||||
9
.github/actions/spelling/allow/apis.txt
vendored
9
.github/actions/spelling/allow/apis.txt
vendored
@@ -34,6 +34,7 @@ DNE
|
||||
DONTADDTORECENT
|
||||
DWMSBT
|
||||
DWMWA
|
||||
DWMWA
|
||||
DWORDLONG
|
||||
endfor
|
||||
ENDSESSION
|
||||
@@ -54,8 +55,6 @@ GETMOUSEHOVERTIME
|
||||
Hashtable
|
||||
HIGHCONTRASTON
|
||||
HIGHCONTRASTW
|
||||
hinternet
|
||||
HINTERNET
|
||||
hotkeys
|
||||
href
|
||||
hrgn
|
||||
@@ -159,7 +158,6 @@ rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
RLO
|
||||
ROOTOWNER
|
||||
roundf
|
||||
RSHIFT
|
||||
@@ -214,11 +212,8 @@ userenv
|
||||
USEROBJECTFLAGS
|
||||
Viewbox
|
||||
virtualalloc
|
||||
vsnwprintf
|
||||
wcsstr
|
||||
wcstoui
|
||||
WDJ
|
||||
winhttp
|
||||
winmain
|
||||
winsta
|
||||
winstamin
|
||||
@@ -226,7 +221,6 @@ wmemcmp
|
||||
wpc
|
||||
WSF
|
||||
wsregex
|
||||
WWH
|
||||
wwinmain
|
||||
xchg
|
||||
XDocument
|
||||
@@ -252,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/excludes.txt
vendored
2
.github/actions/spelling/excludes.txt
vendored
@@ -109,10 +109,8 @@
|
||||
^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$
|
||||
|
||||
1
.github/actions/spelling/expect/alphabet.txt
vendored
1
.github/actions/spelling/expect/alphabet.txt
vendored
@@ -18,7 +18,6 @@ BBBBBBBB
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
efg
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
|
||||
51
.github/actions/spelling/expect/expect.txt
vendored
51
.github/actions/spelling/expect/expect.txt
vendored
@@ -2,7 +2,6 @@ aabbcc
|
||||
ABANDONFONT
|
||||
abbcc
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXY
|
||||
ABCF
|
||||
abgr
|
||||
abi
|
||||
ABORTIFHUNG
|
||||
@@ -38,12 +37,10 @@ ansicpg
|
||||
ANSISYS
|
||||
ANSISYSRC
|
||||
ANSISYSSC
|
||||
answerback
|
||||
antialiasing
|
||||
ANull
|
||||
anycpu
|
||||
APARTMENTTHREADED
|
||||
APCA
|
||||
APCs
|
||||
APIENTRY
|
||||
apiset
|
||||
@@ -178,6 +175,7 @@ CConsole
|
||||
CConversion
|
||||
CCRT
|
||||
cdd
|
||||
CDeclaration
|
||||
CEdit
|
||||
CELLSIZE
|
||||
cfae
|
||||
@@ -195,8 +193,7 @@ chh
|
||||
chk
|
||||
CHT
|
||||
Cic
|
||||
cielab
|
||||
Cielab
|
||||
CLA
|
||||
Clcompile
|
||||
CLE
|
||||
cleartype
|
||||
@@ -260,7 +257,6 @@ condrv
|
||||
conechokey
|
||||
conemu
|
||||
configurability
|
||||
confusables
|
||||
conhost
|
||||
conime
|
||||
conimeinfo
|
||||
@@ -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
|
||||
@@ -412,7 +409,6 @@ DECAUPSS
|
||||
DECAWM
|
||||
DECBKM
|
||||
DECCARA
|
||||
DECCIR
|
||||
DECCKM
|
||||
DECCKSR
|
||||
DECCOLM
|
||||
@@ -442,12 +438,9 @@ DECRC
|
||||
DECREQTPARM
|
||||
DECRLM
|
||||
DECRPM
|
||||
DECRQCRA
|
||||
DECRQM
|
||||
DECRQPSR
|
||||
DECRQSS
|
||||
DECRQTSR
|
||||
DECRSPS
|
||||
decrst
|
||||
DECSACE
|
||||
DECSASD
|
||||
@@ -468,7 +461,6 @@ DECSTBM
|
||||
DECSTGLT
|
||||
DECSTR
|
||||
DECSWL
|
||||
DECTABSR
|
||||
DECTCEM
|
||||
DECXCPR
|
||||
DEFAPP
|
||||
@@ -488,6 +480,7 @@ defterm
|
||||
DELAYLOAD
|
||||
DELETEONRELEASE
|
||||
Delt
|
||||
demoable
|
||||
depersist
|
||||
deprioritized
|
||||
deserializers
|
||||
@@ -544,6 +537,7 @@ DSSCL
|
||||
DSwap
|
||||
DTest
|
||||
DTTERM
|
||||
DUMMYUNIONNAME
|
||||
dup'ed
|
||||
dvi
|
||||
dwl
|
||||
@@ -587,7 +581,6 @@ ENU
|
||||
ENUMLOGFONT
|
||||
ENUMLOGFONTEX
|
||||
enumranges
|
||||
EOK
|
||||
eplace
|
||||
EPres
|
||||
EQU
|
||||
@@ -597,6 +590,7 @@ ETW
|
||||
EUDC
|
||||
EVENTID
|
||||
eventing
|
||||
everytime
|
||||
evflags
|
||||
evt
|
||||
execd
|
||||
@@ -618,12 +612,8 @@ FACESIZE
|
||||
FAILIFTHERE
|
||||
fastlink
|
||||
fcharset
|
||||
FDEA
|
||||
fdw
|
||||
FECF
|
||||
FEEF
|
||||
fesb
|
||||
FFAF
|
||||
FFDE
|
||||
FFrom
|
||||
fgbg
|
||||
@@ -820,8 +810,6 @@ hkl
|
||||
HKLM
|
||||
hlocal
|
||||
hlsl
|
||||
HMB
|
||||
HMK
|
||||
hmod
|
||||
hmodule
|
||||
hmon
|
||||
@@ -911,6 +899,7 @@ INSERTMODE
|
||||
INTERACTIVITYBASE
|
||||
INTERCEPTCOPYPASTE
|
||||
INTERNALNAME
|
||||
inthread
|
||||
intsafe
|
||||
INVALIDARG
|
||||
INVALIDATERECT
|
||||
@@ -1129,7 +1118,6 @@ Mip
|
||||
MMBB
|
||||
mmcc
|
||||
MMCPL
|
||||
MMIX
|
||||
mmsystem
|
||||
MNC
|
||||
MNOPQ
|
||||
@@ -1268,12 +1256,14 @@ ntm
|
||||
nto
|
||||
ntrtl
|
||||
ntstatus
|
||||
ntsubauth
|
||||
NTSYSCALLAPI
|
||||
nttree
|
||||
nturtl
|
||||
ntuser
|
||||
NTVDM
|
||||
ntverp
|
||||
NTWIN
|
||||
nugetversions
|
||||
nullability
|
||||
nullness
|
||||
@@ -1294,8 +1284,6 @@ OEMFONT
|
||||
OEMFORMAT
|
||||
OEMs
|
||||
offboarded
|
||||
oklab
|
||||
Oklab
|
||||
OLEAUT
|
||||
OLECHAR
|
||||
onecore
|
||||
@@ -1314,6 +1302,8 @@ opencode
|
||||
opencon
|
||||
openconsole
|
||||
openconsoleproxy
|
||||
OPENIF
|
||||
OPENLINK
|
||||
openps
|
||||
openvt
|
||||
ORIGINALFILENAME
|
||||
@@ -1366,7 +1356,9 @@ pcg
|
||||
pch
|
||||
PCIDLIST
|
||||
PCIS
|
||||
PCLIENT
|
||||
PCLONG
|
||||
PCOBJECT
|
||||
pcon
|
||||
PCONSOLE
|
||||
PCONSOLEENDTASK
|
||||
@@ -1378,6 +1370,7 @@ pcshell
|
||||
PCSHORT
|
||||
PCSR
|
||||
PCSTR
|
||||
PCUNICODE
|
||||
PCWCH
|
||||
PCWCHAR
|
||||
PCWSTR
|
||||
@@ -1426,6 +1419,7 @@ PLOGICAL
|
||||
pnm
|
||||
PNMLINK
|
||||
pntm
|
||||
PNTSTATUS
|
||||
POBJECT
|
||||
Podcast
|
||||
POINTSLIST
|
||||
@@ -1443,7 +1437,9 @@ PPEB
|
||||
ppf
|
||||
ppguid
|
||||
ppidl
|
||||
pplx
|
||||
PPROC
|
||||
PPROCESS
|
||||
ppropvar
|
||||
ppsi
|
||||
ppsl
|
||||
@@ -1507,6 +1503,7 @@ ptrs
|
||||
ptsz
|
||||
PTYIn
|
||||
PUCHAR
|
||||
PUNICODE
|
||||
pwch
|
||||
PWDDMCONSOLECONTEXT
|
||||
pws
|
||||
@@ -1568,6 +1565,7 @@ REGISTEROS
|
||||
REGISTERVDM
|
||||
regkey
|
||||
REGSTR
|
||||
reingest
|
||||
RELBINPATH
|
||||
remoting
|
||||
renamer
|
||||
@@ -1579,7 +1577,6 @@ replatformed
|
||||
Replymessage
|
||||
repositorypath
|
||||
Requiresx
|
||||
rerasterize
|
||||
rescap
|
||||
Resequence
|
||||
RESETCONTENT
|
||||
@@ -1776,13 +1773,10 @@ srcsrv
|
||||
SRCSRVTRG
|
||||
srctool
|
||||
srect
|
||||
srgb
|
||||
Srgb
|
||||
srv
|
||||
srvinit
|
||||
srvpipe
|
||||
ssa
|
||||
startdir
|
||||
STARTF
|
||||
STARTUPINFO
|
||||
STARTUPINFOEX
|
||||
@@ -1792,7 +1786,6 @@ STARTWPARMS
|
||||
STARTWPARMSA
|
||||
STARTWPARMSW
|
||||
Statusline
|
||||
stb
|
||||
stdafx
|
||||
STDAPI
|
||||
stdc
|
||||
@@ -1866,7 +1859,6 @@ TDP
|
||||
TEAMPROJECT
|
||||
tearoff
|
||||
Teb
|
||||
Techo
|
||||
tellp
|
||||
teraflop
|
||||
terminalcore
|
||||
@@ -1958,7 +1950,6 @@ trx
|
||||
tsattrs
|
||||
tsf
|
||||
tsgr
|
||||
tsm
|
||||
TStr
|
||||
TSTRFORMAT
|
||||
TSub
|
||||
@@ -2010,6 +2001,7 @@ unittesting
|
||||
unittests
|
||||
unk
|
||||
unknwn
|
||||
unmark
|
||||
UNORM
|
||||
unparseable
|
||||
unregistering
|
||||
@@ -2122,6 +2114,7 @@ WDDMCONSOLECONTEXT
|
||||
wdm
|
||||
webpage
|
||||
websites
|
||||
websockets
|
||||
wekyb
|
||||
wex
|
||||
wextest
|
||||
|
||||
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])
|
||||
|
||||
@@ -35,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#
|
||||
|
||||
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 }}"
|
||||
4
.vscode/settings.json
vendored
4
.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",
|
||||
@@ -106,4 +106,4 @@
|
||||
"**/packages/**": true,
|
||||
"**/Generated Files/**": 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,8 +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
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|Any CPU = AuditMode|Any CPU
|
||||
@@ -2772,32 +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|ARM64.Build.0 = Debug|ARM64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x64.Build.0 = Debug|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x86.Build.0 = 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|ARM64.Build.0 = Release|ARM64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x64.ActiveCfg = Release|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x64.Build.0 = Release|x64
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2903,7 +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}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
||||
@@ -55,7 +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 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,12 +254,16 @@ 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
|
||||
@@ -275,17 +297,7 @@ jobs:
|
||||
displayName: Publish Artifact (appx)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/appx
|
||||
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)
|
||||
|
||||
- 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)/unpackaged"
|
||||
displayName: Build Unpackaged Distribution
|
||||
|
||||
- publish: $(Build.ArtifactStagingDirectory)/unpackaged
|
||||
artifact: unpackaged-$(BuildPlatform)-$(BuildConfiguration)
|
||||
displayName: Publish Artifact (unpackaged)
|
||||
|
||||
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion)
|
||||
- ${{ if eq(parameters.buildConPTY, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy ConPTY to Artifacts
|
||||
@@ -303,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
|
||||
@@ -317,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
|
||||
@@ -335,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') }}:
|
||||
@@ -356,9 +373,9 @@ jobs:
|
||||
disableOutputRedirect: true
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Artifacts ${{ platform }}
|
||||
displayName: Download Artifacts ${{ platform }} $(TerminalTargetWindowsVersion)
|
||||
inputs:
|
||||
artifactName: appx-${{ platform }}-Release
|
||||
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.
|
||||
@@ -368,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 "$(System.ArtifactsDirectory)" -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
|
||||
@@ -409,7 +426,7 @@ jobs:
|
||||
displayName: 'Publish Artifact: appxbundle-signed'
|
||||
inputs:
|
||||
PathtoPublish: $(System.ArtifactsDirectory)\bundle
|
||||
ArtifactName: appxbundle-signed
|
||||
ArtifactName: appxbundle-signed-$(TerminalTargetWindowsVersion)
|
||||
|
||||
- ${{ if eq(parameters.buildConPTY, true) }}:
|
||||
- job: PackageAndSignConPTY
|
||||
@@ -434,7 +451,7 @@ jobs:
|
||||
- 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
|
||||
@@ -525,7 +542,7 @@ jobs:
|
||||
- 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
|
||||
@@ -623,10 +640,11 @@ jobs:
|
||||
|
||||
# Download the appx-PLATFORM-CONFIG-VERSION artifact for every platform/version combo
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Symbols ${{ platform }}
|
||||
inputs:
|
||||
artifactName: appx-${{ platform }}-Release
|
||||
- ${{ 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.
|
||||
@@ -686,7 +704,7 @@ jobs:
|
||||
- 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
|
||||
@@ -695,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
|
||||
@@ -703,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 }}
|
||||
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
12
custom.props
12
custom.props
@@ -2,6 +2,18 @@
|
||||
<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>
|
||||
|
||||
@@ -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.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.3" 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"
|
||||
@@ -1741,8 +1726,7 @@
|
||||
"enum": [
|
||||
"always",
|
||||
"hover",
|
||||
"never",
|
||||
"activeOnly"
|
||||
"never"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1823,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" ]
|
||||
}
|
||||
},
|
||||
@@ -2093,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.",
|
||||
@@ -2170,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.",
|
||||
@@ -2575,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,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,10 +4,7 @@
|
||||
#include "precomp.h"
|
||||
#include "Row.hpp"
|
||||
|
||||
#include <til/unicode.h>
|
||||
|
||||
#include "textBuffer.hpp"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
|
||||
// The STL is missing a std::iota_n analogue for std::iota, so I made my own.
|
||||
template<typename OutIt, typename Diff, typename T>
|
||||
@@ -88,6 +85,19 @@ ROW::ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, c
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
_wrapForced = wrap;
|
||||
@@ -141,46 +151,84 @@ void ROW::_init() noexcept
|
||||
std::iota(_charOffsets.begin(), _charOffsets.end(), uint16_t{ 0 });
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
_attr.resize_trailing_extent(rowWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void ROW::TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth)
|
||||
{
|
||||
_attr = attr;
|
||||
_attr.resize_trailing_extent(gsl::narrow<uint16_t>(newWidth));
|
||||
}
|
||||
|
||||
// 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:
|
||||
@@ -263,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;
|
||||
@@ -327,245 +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
|
||||
{
|
||||
size_t ch = chBeg;
|
||||
|
||||
for (const auto& s : til::utf16_iterator{ chars })
|
||||
{
|
||||
const auto wide = til::at(s, 0) < 0x80 ? false : IsGlyphFullWidth(s);
|
||||
const auto colEndNew = gsl::narrow_cast<uint16_t>(colEnd + 1u + wide);
|
||||
if (colEndNew > colLimit)
|
||||
{
|
||||
colEndDirty = colLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch);
|
||||
if (wide)
|
||||
{
|
||||
til::at(row._charOffsets, colEnd++) = gsl::narrow_cast<uint16_t>(ch | CharOffsetsTrailer);
|
||||
}
|
||||
|
||||
colEndDirty = colEnd;
|
||||
ch += s.size();
|
||||
}
|
||||
|
||||
charsConsumed = ch - chBeg;
|
||||
}
|
||||
|
||||
til::CoordType ROW::CopyRangeFrom(til::CoordType columnBegin, til::CoordType columnLimit, const ROW& other, til::CoordType& otherBegin, til::CoordType otherLimit)
|
||||
try
|
||||
{
|
||||
const auto otherColBeg = other._clampedColumnInclusive(otherBegin);
|
||||
const auto otherColLimit = other._clampedColumnInclusive(otherLimit);
|
||||
std::span<uint16_t> charOffsets;
|
||||
std::wstring_view chars;
|
||||
|
||||
if (otherColBeg < otherColLimit)
|
||||
{
|
||||
charOffsets = other._charOffsets.subspan(otherColBeg, static_cast<size_t>(otherColLimit) - otherColBeg + 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 = { other._chars.data() + charsOffset, other._chars.size() - charsOffset };
|
||||
}
|
||||
|
||||
WriteHelper h{ *this, columnBegin, columnLimit, chars };
|
||||
if (!h.IsValid())
|
||||
{
|
||||
return h.colBeg;
|
||||
}
|
||||
// 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.
|
||||
if (charOffsets.size() < 2 || WI_IsFlagSet(charOffsets.front(), CharOffsetsTrailer))
|
||||
{
|
||||
assert(false);
|
||||
otherBegin = other.size();
|
||||
return h.colBeg;
|
||||
}
|
||||
h.CopyRangeFrom(charOffsets);
|
||||
h.Finish();
|
||||
|
||||
otherBegin += h.colEnd - h.colBeg;
|
||||
return h.colEndDirty;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Reset(TextAttribute{});
|
||||
throw;
|
||||
}
|
||||
|
||||
[[msvc::forceinline]] void ROW::WriteHelper::CopyRangeFrom(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);
|
||||
|
||||
// Now with the `colEndInput` figured out, we can easily copy the `charOffsets` into the `_charOffsets`.
|
||||
// It's possible to use SIMD for this loop for extra perf gains. Something like this for SSE2 (~8x faster):
|
||||
// const auto in = _mm_loadu_si128(...);
|
||||
// const auto off = _mm_and_epi32(in, _mm_set1_epi16(CharOffsetsMask));
|
||||
// const auto trailer = _mm_and_epi32(in, _mm_set1_epi16(CharOffsetsTrailer));
|
||||
// const auto out = _mm_or_epi32(_mm_add_epi16(off, _mm_set1_epi16(inToOutOffset)), trailer);
|
||||
// _mm_store_si128(..., out);
|
||||
for (uint16_t i = 0; i < colEndInput; ++i, ++colEnd)
|
||||
{
|
||||
const auto ch = til::at(charOffsets, i);
|
||||
const auto off = ch & CharOffsetsMask;
|
||||
const auto trailer = ch & CharOffsetsTrailer;
|
||||
til::at(row._charOffsets, colEnd) = gsl::narrow_cast<uint16_t>((off + inToOutOffset) | trailer);
|
||||
}
|
||||
|
||||
colEndDirty = gsl::narrow_cast<uint16_t>(colBeg + colEndDirtyInput);
|
||||
charsConsumed = endOffset - baseOffset;
|
||||
}
|
||||
|
||||
[[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.
|
||||
// Algorithm explanation
|
||||
//
|
||||
// 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)
|
||||
// Task:
|
||||
// Replace the characters in cells [colBeg, colEnd) with a single `width`-wide glyph consisting of `chars`.
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,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
|
||||
{
|
||||
@@ -591,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)
|
||||
{
|
||||
@@ -606,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;
|
||||
@@ -639,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();
|
||||
@@ -799,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);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ Revision History:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include <til/rle.h>
|
||||
|
||||
#include "LineRendition.hpp"
|
||||
@@ -35,28 +37,6 @@ 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 = 0; // 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
|
||||
};
|
||||
|
||||
class ROW final
|
||||
{
|
||||
public:
|
||||
@@ -66,9 +46,11 @@ public:
|
||||
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;
|
||||
@@ -77,25 +59,19 @@ public:
|
||||
LineRendition GetLineRendition() const 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);
|
||||
|
||||
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);
|
||||
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);
|
||||
til::CoordType CopyRangeFrom(til::CoordType columnBegin, til::CoordType columnLimit, const ROW& other, til::CoordType& otherBegin, til::CoordType otherLimit);
|
||||
|
||||
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;
|
||||
@@ -113,50 +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 CopyRangeFrom(const std::span<const uint16_t>& charOffsets) 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.
|
||||
@@ -170,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
|
||||
|
||||
@@ -13,6 +13,75 @@
|
||||
#include "../types/inc/convert.hpp"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct BufferAllocator
|
||||
{
|
||||
BufferAllocator(til::size sz)
|
||||
{
|
||||
const auto w = gsl::narrow<uint16_t>(sz.width);
|
||||
const auto h = gsl::narrow<uint16_t>(sz.height);
|
||||
|
||||
const auto charsBytes = w * sizeof(wchar_t);
|
||||
// The ROW::_indices array stores 1 more item than the buffer is wide.
|
||||
// That extra column stores the past-the-end _chars pointer.
|
||||
const auto indicesBytes = w * sizeof(uint16_t) + sizeof(uint16_t);
|
||||
const auto rowStride = charsBytes + indicesBytes;
|
||||
// 65535*65535 cells would result in a charsAreaSize of 8GiB.
|
||||
// --> Use uint64_t so that we can safely do our calculations even on x86.
|
||||
const auto allocSize = gsl::narrow<size_t>(::base::strict_cast<uint64_t>(rowStride) * ::base::strict_cast<uint64_t>(h));
|
||||
|
||||
_buffer = wil::unique_virtualalloc_ptr<std::byte>{ static_cast<std::byte*>(VirtualAlloc(nullptr, allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) };
|
||||
THROW_IF_NULL_ALLOC(_buffer);
|
||||
|
||||
_data = std::span{ _buffer.get(), allocSize }.begin();
|
||||
_rowStride = rowStride;
|
||||
_indicesOffset = charsBytes;
|
||||
_width = w;
|
||||
_height = h;
|
||||
}
|
||||
|
||||
BufferAllocator& operator++() noexcept
|
||||
{
|
||||
_data += _rowStride;
|
||||
return *this;
|
||||
}
|
||||
|
||||
wchar_t* chars() const noexcept
|
||||
{
|
||||
return til::bit_cast<wchar_t*>(&*_data);
|
||||
}
|
||||
|
||||
uint16_t* indices() const noexcept
|
||||
{
|
||||
return til::bit_cast<uint16_t*>(&*(_data + _indicesOffset));
|
||||
}
|
||||
|
||||
uint16_t width() const noexcept
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
uint16_t height() const noexcept
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
wil::unique_virtualalloc_ptr<std::byte>&& take() noexcept
|
||||
{
|
||||
return std::move(_buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
wil::unique_virtualalloc_ptr<std::byte> _buffer;
|
||||
std::span<std::byte>::iterator _data;
|
||||
size_t _rowStride;
|
||||
size_t _indicesOffset;
|
||||
uint16_t _width;
|
||||
uint16_t _height;
|
||||
};
|
||||
}
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace Microsoft::Console::Types;
|
||||
|
||||
@@ -42,7 +111,16 @@ TextBuffer::TextBuffer(til::size screenBufferSize,
|
||||
// Guard against resizing the text buffer to 0 columns/rows, which would break being able to insert text.
|
||||
screenBufferSize.width = std::max(screenBufferSize.width, 1);
|
||||
screenBufferSize.height = std::max(screenBufferSize.height, 1);
|
||||
_charBuffer = _allocateBuffer(screenBufferSize, _currentAttributes, _storage);
|
||||
|
||||
BufferAllocator allocator{ screenBufferSize };
|
||||
|
||||
_storage.reserve(allocator.height());
|
||||
for (til::CoordType i = 0; i < screenBufferSize.height; ++i, ++allocator)
|
||||
{
|
||||
_storage.emplace_back(allocator.chars(), allocator.indices(), allocator.width(), _currentAttributes);
|
||||
}
|
||||
|
||||
_charBuffer = allocator.take();
|
||||
_UpdateSize();
|
||||
}
|
||||
|
||||
@@ -272,6 +350,10 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
|
||||
// - false otherwise (out of memory)
|
||||
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
|
||||
{
|
||||
// This function corrects most errors. If this is false, we had an uncorrectable one which
|
||||
// older versions of conhost simply let pass by unflinching.
|
||||
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
|
||||
|
||||
auto fSuccess = true;
|
||||
// Now compensate if we don't have enough space for the upcoming double byte sequence
|
||||
// We only need to compensate for leading bytes
|
||||
@@ -294,32 +376,6 @@ bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
void TextBuffer::ConsumeGrapheme(std::wstring_view& chars) noexcept
|
||||
{
|
||||
// This function is supposed to mirror the behavior of ROW::Write, when it reads characters off of `chars`.
|
||||
// (I know that a UTF-16 code point is not a grapheme, but that's what we're working towards.)
|
||||
chars = til::utf16_pop(chars);
|
||||
}
|
||||
|
||||
// This function is intended for writing regular "lines" of text and only the `state.text` and`state.columnBegin`
|
||||
// fields are being used, whereas `state.columnLimit` is automatically overwritten by the line width of the given row.
|
||||
// This allows this function to automatically set the wrap-forced field of the row, which is also the return value.
|
||||
// The return value indicates to the caller whether the cursor should be moved to the next line.
|
||||
void TextBuffer::WriteLine(til::CoordType row, bool wrapAtEOL, const TextAttribute& attributes, RowWriteState& state)
|
||||
{
|
||||
auto& r = GetRowByOffset(row);
|
||||
|
||||
r.ReplaceText(state);
|
||||
r.ReplaceAttributes(state.columnBegin, state.columnEnd, attributes);
|
||||
|
||||
if (state.columnEnd >= state.columnLimit)
|
||||
{
|
||||
r.SetWrapForced(wrapAtEOL);
|
||||
}
|
||||
|
||||
TriggerRedraw(Viewport::FromExclusive({ state.columnBeginDirty, row, state.columnEndDirty, row + 1 }));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Writes cells to the output buffer. Writes at the cursor.
|
||||
// Arguments:
|
||||
@@ -693,37 +749,6 @@ const Viewport TextBuffer::GetSize() const noexcept
|
||||
return _size;
|
||||
}
|
||||
|
||||
wil::unique_virtualalloc_ptr<std::byte> TextBuffer::_allocateBuffer(til::size sz, const TextAttribute& attributes, std::vector<ROW>& rows)
|
||||
{
|
||||
const auto w = gsl::narrow<uint16_t>(sz.width);
|
||||
const auto h = gsl::narrow<uint16_t>(sz.height);
|
||||
|
||||
const auto charsBytes = w * sizeof(wchar_t);
|
||||
// The ROW::_indices array stores 1 more item than the buffer is wide.
|
||||
// That extra column stores the past-the-end _chars pointer.
|
||||
const auto indicesBytes = w * sizeof(uint16_t) + sizeof(uint16_t);
|
||||
const auto rowStride = charsBytes + indicesBytes;
|
||||
// 65535*65535 cells would result in a charsAreaSize of 8GiB.
|
||||
// --> Use uint64_t so that we can safely do our calculations even on x86.
|
||||
const auto allocSize = gsl::narrow<size_t>(::base::strict_cast<uint64_t>(rowStride) * ::base::strict_cast<uint64_t>(h));
|
||||
|
||||
auto buffer = wil::unique_virtualalloc_ptr<std::byte>{ static_cast<std::byte*>(VirtualAlloc(nullptr, allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) };
|
||||
THROW_IF_NULL_ALLOC(buffer);
|
||||
|
||||
auto data = std::span{ buffer.get(), allocSize }.begin();
|
||||
|
||||
rows.resize(h);
|
||||
for (auto& row : rows)
|
||||
{
|
||||
const auto chars = til::bit_cast<wchar_t*>(&*data);
|
||||
const auto indices = til::bit_cast<uint16_t*>(&*(data + charsBytes));
|
||||
row = { chars, indices, w, attributes };
|
||||
data += rowStride;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void TextBuffer::_UpdateSize()
|
||||
{
|
||||
_size = Viewport::FromDimensions({ _storage.at(0).size(), gsl::narrow<til::CoordType>(_storage.size()) });
|
||||
@@ -950,55 +975,37 @@ void TextBuffer::Reset()
|
||||
|
||||
try
|
||||
{
|
||||
BufferAllocator allocator{ newSize };
|
||||
|
||||
const auto currentSize = GetSize().Dimensions();
|
||||
const auto attributes = GetCurrentAttributes();
|
||||
|
||||
til::CoordType TopRow = 0; // new top row of the screen buffer
|
||||
if (newSize.height <= GetCursor().GetPosition().y)
|
||||
{
|
||||
TopRow = GetCursor().GetPosition().y - newSize.height + 1;
|
||||
}
|
||||
const auto TopRowIndex = gsl::narrow_cast<size_t>(_firstRow + TopRow) % _storage.size();
|
||||
const auto TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.height;
|
||||
|
||||
std::vector<ROW> newStorage;
|
||||
auto newBuffer = _allocateBuffer(newSize, _currentAttributes, newStorage);
|
||||
// rotate rows until the top row is at index 0
|
||||
std::rotate(_storage.begin(), _storage.begin() + TopRowIndex, _storage.end());
|
||||
_SetFirstRowIndex(0);
|
||||
|
||||
// This basically imitates a std::rotate_copy(first, mid, last), but uses ROW::CopyRangeFrom() to do the copying.
|
||||
// realloc in the Y direction
|
||||
// remove rows if we're shrinking
|
||||
_storage.resize(allocator.height());
|
||||
|
||||
// realloc in the X direction
|
||||
for (auto& it : _storage)
|
||||
{
|
||||
const auto first = _storage.begin();
|
||||
const auto last = _storage.end();
|
||||
const auto mid = first + TopRowIndex;
|
||||
auto dest = newStorage.begin();
|
||||
|
||||
std::span<ROW> sourceRanges[]{
|
||||
{ mid, last },
|
||||
{ first, mid },
|
||||
};
|
||||
|
||||
// Ensure we don't copy more from `_storage` than fit into `newStorage`.
|
||||
if (sourceRanges[0].size() > newStorage.size())
|
||||
{
|
||||
sourceRanges[0] = sourceRanges[0].subspan(0, newStorage.size());
|
||||
}
|
||||
if (const auto remaining = newStorage.size() - sourceRanges[0].size(); sourceRanges[1].size() > remaining)
|
||||
{
|
||||
sourceRanges[1] = sourceRanges[1].subspan(0, remaining);
|
||||
}
|
||||
|
||||
for (const auto& sourceRange : sourceRanges)
|
||||
{
|
||||
for (const auto& oldRow : sourceRange)
|
||||
{
|
||||
til::CoordType begin = 0;
|
||||
dest->CopyRangeFrom(0, til::CoordTypeMax, oldRow, begin, til::CoordTypeMax);
|
||||
dest->TransferAttributes(oldRow.Attributes(), newSize.width);
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
it.Resize(allocator.chars(), allocator.indices(), allocator.width(), attributes);
|
||||
++allocator;
|
||||
}
|
||||
|
||||
_charBuffer = std::move(newBuffer);
|
||||
_storage = std::move(newStorage);
|
||||
|
||||
_SetFirstRowIndex(0);
|
||||
// Update the cached size value
|
||||
_UpdateSize();
|
||||
|
||||
_charBuffer = allocator.take();
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
@@ -2637,9 +2644,9 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
|
||||
// - Adds or updates a hyperlink in our hyperlink table
|
||||
// Arguments:
|
||||
// - The hyperlink URI, the hyperlink id (could be new or old)
|
||||
void TextBuffer::AddHyperlinkToMap(LinkData data, uint16_t id)
|
||||
void TextBuffer::AddHyperlinkToMap(std::wstring_view uri, uint16_t id)
|
||||
{
|
||||
_hyperlinkMap[id] = data;
|
||||
_hyperlinkMap[id] = uri;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -2648,7 +2655,7 @@ void TextBuffer::AddHyperlinkToMap(LinkData data, uint16_t id)
|
||||
// - The hyperlink ID
|
||||
// Return Value:
|
||||
// - The URI
|
||||
const LinkData& TextBuffer::GetHyperlinkUriFromId(uint16_t id) const
|
||||
std::wstring TextBuffer::GetHyperlinkUriFromId(uint16_t id) const
|
||||
{
|
||||
return _hyperlinkMap.at(id);
|
||||
}
|
||||
|
||||
@@ -64,20 +64,6 @@ namespace Microsoft::Console::Render
|
||||
class Renderer;
|
||||
}
|
||||
|
||||
enum class LinkType
|
||||
{
|
||||
None = 0,
|
||||
Url,
|
||||
SendInput,
|
||||
};
|
||||
|
||||
struct LinkData
|
||||
{
|
||||
std::wstring payload;
|
||||
LinkType type{ LinkType::None };
|
||||
bool IsUrl() const { return type == LinkType::Url; };
|
||||
};
|
||||
|
||||
class TextBuffer final
|
||||
{
|
||||
public:
|
||||
@@ -103,9 +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 WriteLine(til::CoordType row, bool wrapAtEOL, const TextAttribute& attributes, RowWriteState& state);
|
||||
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt);
|
||||
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt,
|
||||
@@ -181,8 +164,8 @@ public:
|
||||
const std::vector<til::inclusive_rect> GetTextRects(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const;
|
||||
std::vector<til::point_span> GetTextSpans(til::point start, til::point end, bool blockSelection, bool bufferCoordinates) const;
|
||||
|
||||
void AddHyperlinkToMap(LinkData uri, uint16_t id);
|
||||
const LinkData& GetHyperlinkUriFromId(uint16_t id) const;
|
||||
void AddHyperlinkToMap(std::wstring_view uri, uint16_t id);
|
||||
std::wstring GetHyperlinkUriFromId(uint16_t id) const;
|
||||
uint16_t GetHyperlinkId(std::wstring_view uri, std::wstring_view id);
|
||||
void RemoveHyperlinkFromMap(uint16_t id) noexcept;
|
||||
std::wstring GetCustomIdFromId(uint16_t id) const;
|
||||
@@ -233,8 +216,6 @@ public:
|
||||
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const til::CoordType firstRow, const til::CoordType lastRow) const;
|
||||
|
||||
private:
|
||||
static wil::unique_virtualalloc_ptr<std::byte> _allocateBuffer(til::size sz, const TextAttribute& attributes, std::vector<ROW>& rows);
|
||||
|
||||
void _UpdateSize();
|
||||
void _SetFirstRowIndex(const til::CoordType FirstRowIndex) noexcept;
|
||||
til::point _GetPreviousFromCursor() const noexcept;
|
||||
@@ -256,7 +237,7 @@ private:
|
||||
|
||||
Microsoft::Console::Render::Renderer& _renderer;
|
||||
|
||||
std::unordered_map<uint16_t, LinkData> _hyperlinkMap;
|
||||
std::unordered_map<uint16_t, std::wstring> _hyperlinkMap;
|
||||
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
|
||||
uint16_t _currentHyperlinkId = 1;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -60,4 +60,4 @@
|
||||
<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;
|
||||
@@ -659,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.
|
||||
@@ -695,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;
|
||||
@@ -737,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
|
||||
}
|
||||
@@ -774,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;
|
||||
}
|
||||
}
|
||||
@@ -791,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;
|
||||
}
|
||||
|
||||
@@ -1055,95 +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
|
||||
auto request = winrt::make_self<implementation::WindowRequestedArgs>(window == L"-1" ? 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();
|
||||
UInt32 ShowWindowCommand { get; };
|
||||
};
|
||||
|
||||
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,60 +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();
|
||||
std::wstring pathRootAsString{ modulePath.wstring() };
|
||||
|
||||
return Utils::CreateV5Uuid(processRootHashedGuidBase, std::as_bytes(std::span{ pathRootAsString }));
|
||||
}();
|
||||
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
|
||||
@@ -64,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);
|
||||
@@ -269,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 (...)
|
||||
{
|
||||
@@ -308,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
|
||||
@@ -410,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()
|
||||
@@ -427,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()
|
||||
@@ -444,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,129 +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);
|
||||
}
|
||||
|
||||
bool AboutDialog::UpdatesAvailable() const
|
||||
{
|
||||
return !_pendingUpdateVersion.empty();
|
||||
}
|
||||
|
||||
winrt::hstring AboutDialog::PendingUpdateVersion() const
|
||||
{
|
||||
return _pendingUpdateVersion;
|
||||
}
|
||||
|
||||
void AboutDialog::_SetPendingUpdateVersion(const winrt::hstring& version)
|
||||
{
|
||||
_pendingUpdateVersion = version;
|
||||
_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"PendingUpdateVersion" });
|
||||
_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"UpdatesAvailable" });
|
||||
}
|
||||
|
||||
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());
|
||||
_SetPendingUpdateVersion({});
|
||||
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());
|
||||
_SetPendingUpdateVersion(L"X.Y.Z");
|
||||
#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)
|
||||
{
|
||||
const auto update = updates.GetAt(0);
|
||||
const auto version = update.Package().Id().Version();
|
||||
const auto str = fmt::format(FMT_COMPILE(L"{}.{}.{}"), version.Major, version.Minor, version.Build);
|
||||
_SetPendingUpdateVersion(winrt::hstring{ str });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// do nothing on failure
|
||||
}
|
||||
|
||||
co_await wil::resume_foreground(strongThis->Dispatcher());
|
||||
CheckingForUpdates(false);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +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();
|
||||
bool UpdatesAvailable() const;
|
||||
winrt::hstring PendingUpdateVersion() const;
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, CheckingForUpdates, _PropertyChangedHandlers, false);
|
||||
|
||||
private:
|
||||
friend struct AboutDialogT<AboutDialog>; // for Xaml to bind events
|
||||
|
||||
std::chrono::system_clock::time_point _lastUpdateCheck{};
|
||||
winrt::hstring _pendingUpdateVersion;
|
||||
|
||||
void _SetPendingUpdateVersion(const winrt::hstring& pendingUpdateVersion);
|
||||
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,17 +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; };
|
||||
String PendingUpdateVersion { get; };
|
||||
}
|
||||
}
|
||||
@@ -1,64 +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" /> <LineBreak />
|
||||
<Run x:Uid="AboutDialog_VersionLabel" />
|
||||
<Run Text="{x:Bind PendingUpdateVersion, Mode=OneWay}" />
|
||||
</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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "pch.h"
|
||||
#include "App.h"
|
||||
#include "App.g.cpp"
|
||||
#include <CoreWindow.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::ApplicationModel::Activation;
|
||||
@@ -33,31 +32,10 @@ namespace winrt::TerminalApp::implementation
|
||||
if (!dispatcherQueue)
|
||||
{
|
||||
_windowsXamlManager = xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread();
|
||||
|
||||
// As of Process Model v3, terminal windows are all created on their
|
||||
// own threads, but we still initiate XAML for the App on the main
|
||||
// thread. Thing is, just initializing XAML creates a CoreWindow for
|
||||
// us. On Windows 10, that CoreWindow will show up as a visible
|
||||
// window on the taskbar, unless we hide it manually. So, go get it
|
||||
// and do the SW_HIDE thing on it.
|
||||
if (const auto& coreWindow{ winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread() })
|
||||
{
|
||||
if (const auto& interop{ coreWindow.try_as<ICoreWindowInterop>() })
|
||||
{
|
||||
HWND coreHandle{ 0 };
|
||||
interop->get_WindowHandle(&coreHandle);
|
||||
if (coreHandle)
|
||||
{
|
||||
// This prevents an empty "DesktopWindowXamlSource" from
|
||||
// appearing on the taskbar
|
||||
ShowWindow(coreHandle, SW_HIDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FAIL_FAST_MSG("Terminal is not intended to run as a Universal Windows Application");
|
||||
_isUwp = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +77,22 @@ namespace winrt::TerminalApp::implementation
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
void App::OnLaunched(const LaunchActivatedEventArgs& /*e*/)
|
||||
{
|
||||
// We used to support a pure UWP version of the Terminal. This method
|
||||
// was only ever used to do UWP-specific setup of our App.
|
||||
// if this is a UWP... it means its our problem to hook up the content to the window here.
|
||||
if (_isUwp)
|
||||
{
|
||||
auto content = Window::Current().Content();
|
||||
if (content == nullptr)
|
||||
{
|
||||
auto logic = Logic();
|
||||
logic.RunAsUwp(); // Must set UWP status first, settings might change based on it.
|
||||
logic.ReloadSettings();
|
||||
logic.Create();
|
||||
|
||||
auto page = logic.GetRoot().as<TerminalPage>();
|
||||
|
||||
Window::Current().Content(page);
|
||||
Window::Current().Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
private:
|
||||
bool _isUwp = false;
|
||||
winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager _windowsXamlManager = nullptr;
|
||||
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::UI::Xaml::Markup::IXamlMetadataProvider> _providers = winrt::single_threaded_vector<Windows::UI::Xaml::Markup::IXamlMetadataProvider>();
|
||||
bool _bIsClosed = false;
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<Application x:Class="TerminalApp.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:TA="using:TerminalApp"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:primitives="using:Microsoft.UI.Xaml.Controls.Primitives"
|
||||
mc:Ignorable="d">
|
||||
<!--
|
||||
If you want to prove this works, then add `RequestedTheme="Light"` to
|
||||
@@ -46,26 +46,8 @@
|
||||
|
||||
<!-- Suppress top padding -->
|
||||
<Thickness x:Key="TabViewHeaderPadding">9,0,5,0</Thickness>
|
||||
|
||||
<Thickness x:Key="TabViewItemBorderThickness">1,1,1,0</Thickness>
|
||||
|
||||
<!--
|
||||
Disable the EntranceThemeTransition for our muxc:TabView, which would slowly slide in the tabs
|
||||
while the window opens. The difference is especially noticeable if window fade-in transitions are
|
||||
disabled system-wide. On my system this shaves off about 10% of the startup cost and looks better.
|
||||
-->
|
||||
<Style TargetType="primitives:TabViewListView">
|
||||
<Setter Property="ItemContainerTransitions">
|
||||
<Setter.Value>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition />
|
||||
<ContentThemeTransition />
|
||||
<ReorderThemeTransition />
|
||||
</TransitionCollection>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Shadow that can be used by any control. -->
|
||||
<ThemeShadow x:Name="SharedShadow" />
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
|
||||
{
|
||||
auto moved = _MovePane(realArgs);
|
||||
auto moved = _MovePane(realArgs.TabIndex());
|
||||
args.Handled(moved);
|
||||
}
|
||||
}
|
||||
@@ -285,28 +285,6 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleEnablePaneReadOnly(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
activeTab->SetPaneReadOnly(true);
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleDisablePaneReadOnly(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
activeTab->SetPaneReadOnly(false);
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
@@ -613,10 +591,10 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<ToggleCommandPaletteArgs>())
|
||||
{
|
||||
const auto p = LoadCommandPalette();
|
||||
const auto v = p.Visibility() == Visibility::Visible ? Visibility::Collapsed : Visibility::Visible;
|
||||
p.EnableCommandPaletteMode(realArgs.LaunchMode());
|
||||
p.Visibility(v);
|
||||
CommandPalette().EnableCommandPaletteMode(realArgs.LaunchMode());
|
||||
CommandPalette().Visibility(CommandPalette().Visibility() == Visibility::Visible ?
|
||||
Visibility::Collapsed :
|
||||
Visibility::Visible);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
@@ -799,10 +777,9 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleTabSearch(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
const auto p = LoadCommandPalette();
|
||||
p.SetTabs(_tabs, _mruTabs);
|
||||
p.EnableTabSearchMode();
|
||||
p.Visibility(Visibility::Visible);
|
||||
CommandPalette().SetTabs(_tabs, _mruTabs);
|
||||
CommandPalette().EnableTabSearchMode();
|
||||
CommandPalette().Visibility(Visibility::Visible);
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
@@ -812,8 +789,17 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<MoveTabArgs>())
|
||||
{
|
||||
auto moved = _MoveTab(realArgs);
|
||||
actionArgs.Handled(moved);
|
||||
auto direction = realArgs.Direction();
|
||||
if (direction != MoveTabDirection::None)
|
||||
{
|
||||
if (auto focusedTabIndex = _GetFocusedTabIndex())
|
||||
{
|
||||
auto currentTabIndex = focusedTabIndex.value();
|
||||
auto delta = direction == MoveTabDirection::Forward ? 1 : -1;
|
||||
_TryMoveTab(currentTabIndex, currentTabIndex + delta);
|
||||
}
|
||||
}
|
||||
actionArgs.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1145,35 +1131,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSelectCommand(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SelectCommandArgs>())
|
||||
{
|
||||
const auto res = _ApplyToActiveControls([&](auto& control) {
|
||||
control.SelectCommand(realArgs.Direction() == Settings::Model::SelectOutputDirection::Previous);
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
void TerminalPage::_HandleSelectOutput(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SelectOutputArgs>())
|
||||
{
|
||||
const auto res = _ApplyToActiveControls([&](auto& control) {
|
||||
control.SelectOutput(realArgs.Direction() == Settings::Model::SelectOutputDirection::Previous);
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMarkMode(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
@@ -1228,17 +1185,4 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleRestartConnection(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
if (const auto activePane{ activeTab->GetActivePane() })
|
||||
{
|
||||
_restartPaneConnection(activePane);
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ void AppCommandlineArgs::_buildMovePaneParser()
|
||||
if (_movePaneTabIndex >= 0)
|
||||
{
|
||||
movePaneAction.Action(ShortcutAction::MovePane);
|
||||
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex), L"" };
|
||||
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex) };
|
||||
movePaneAction.Args(args);
|
||||
_startupActions.push_back(movePaneAction);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,12 +5,10 @@
|
||||
|
||||
#include "AppLogic.g.h"
|
||||
#include "FindTargetWindowResult.g.h"
|
||||
|
||||
#include "SystemMenuChangeArgs.g.h"
|
||||
#include "Jumplist.h"
|
||||
#include "LanguageProfileNotifier.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
#include "TerminalWindow.h"
|
||||
#include "ContentManager.h"
|
||||
#include "TerminalPage.h"
|
||||
|
||||
#include <inc/cppwinrt_utils.h>
|
||||
#include <ThrottledFunc.h>
|
||||
@@ -38,80 +36,192 @@ namespace winrt::TerminalApp::implementation
|
||||
FindTargetWindowResult(id, L""){};
|
||||
};
|
||||
|
||||
struct AppLogic : AppLogicT<AppLogic>
|
||||
struct SystemMenuChangeArgs : SystemMenuChangeArgsT<SystemMenuChangeArgs>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::hstring, Name, L"");
|
||||
WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add);
|
||||
WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr);
|
||||
|
||||
public:
|
||||
SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) :
|
||||
_Name{ name }, _Action{ action }, _Handler{ handler } {};
|
||||
};
|
||||
|
||||
struct AppLogic : AppLogicT<AppLogic, IInitializeWithWindow>
|
||||
{
|
||||
public:
|
||||
static AppLogic* Current() noexcept;
|
||||
static const Microsoft::Terminal::Settings::Model::CascadiaSettings CurrentAppSettings();
|
||||
|
||||
AppLogic();
|
||||
~AppLogic() = default;
|
||||
|
||||
STDMETHODIMP Initialize(HWND hwnd);
|
||||
|
||||
void Create();
|
||||
bool IsRunningElevated() const noexcept;
|
||||
bool CanDragDrop() const noexcept;
|
||||
bool IsUwp() const noexcept;
|
||||
void RunAsUwp();
|
||||
bool IsElevated() const noexcept;
|
||||
void ReloadSettings();
|
||||
void NotifyRootInitialized();
|
||||
|
||||
bool HasSettingsStartupActions() const noexcept;
|
||||
|
||||
bool ShouldUsePersistedLayout() const;
|
||||
void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts);
|
||||
|
||||
[[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept;
|
||||
|
||||
void Quit();
|
||||
|
||||
bool HasCommandlineArguments() const noexcept;
|
||||
bool HasSettingsStartupActions() const noexcept;
|
||||
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
|
||||
int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd);
|
||||
TerminalApp::FindTargetWindowResult FindTargetWindow(array_view<const winrt::hstring> actions);
|
||||
winrt::hstring ParseCommandlineMessage();
|
||||
bool ShouldExitEarly();
|
||||
|
||||
bool FocusMode() const;
|
||||
bool Fullscreen() const;
|
||||
void Maximized(bool newMaximized);
|
||||
bool AlwaysOnTop() const;
|
||||
bool AutoHideWindow();
|
||||
|
||||
bool ShouldUsePersistedLayout();
|
||||
bool ShouldImmediatelyHandoffToElevated();
|
||||
void HandoffToElevated();
|
||||
hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position);
|
||||
void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector<hstring>& layouts);
|
||||
void IdentifyWindow();
|
||||
void RenameFailed();
|
||||
winrt::hstring WindowName();
|
||||
void WindowName(const winrt::hstring& name);
|
||||
uint64_t WindowId();
|
||||
void WindowId(const uint64_t& id);
|
||||
void SetPersistedLayoutIdx(const uint32_t idx);
|
||||
void SetNumberOfOpenWindows(const uint64_t num);
|
||||
bool IsQuakeWindow() const noexcept;
|
||||
void RequestExitFullscreen();
|
||||
|
||||
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
|
||||
bool CenterOnLaunch();
|
||||
TerminalApp::InitialPosition GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY);
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
|
||||
Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode();
|
||||
bool GetShowTabsInTitlebar();
|
||||
bool GetInitialAlwaysOnTop();
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
|
||||
Windows::UI::Xaml::UIElement GetRoot() noexcept;
|
||||
|
||||
void SetInboundListener();
|
||||
|
||||
hstring Title();
|
||||
void TitlebarClicked();
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
|
||||
void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position);
|
||||
void WindowVisibilityChanged(const bool showOrHide);
|
||||
|
||||
winrt::TerminalApp::TaskbarState TaskbarState();
|
||||
winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush();
|
||||
void WindowActivated(const bool activated);
|
||||
|
||||
bool GetMinimizeToNotificationArea();
|
||||
bool GetAlwaysShowNotificationIcon();
|
||||
bool GetShowTitleInTitlebar();
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
void DismissDialog();
|
||||
|
||||
Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys();
|
||||
|
||||
Microsoft::Terminal::Settings::Model::Theme Theme();
|
||||
bool IsolatedMode();
|
||||
bool AllowHeadless();
|
||||
bool RequestsTrayIcon();
|
||||
|
||||
TerminalApp::TerminalWindow CreateNewWindow();
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// PropertyChanged is surprisingly not a typed event, so we'll define that one manually.
|
||||
// Usually we'd just do
|
||||
// WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
//
|
||||
// But what we're doing here is exposing the Page's PropertyChanged _as
|
||||
// our own event_. It's a FORWARDED_CALLBACK, essentially.
|
||||
winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); }
|
||||
void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); }
|
||||
|
||||
winrt::TerminalApp::ContentManager ContentManager();
|
||||
|
||||
TerminalApp::ParseCommandlineResult GetParseCommandlineMessage(array_view<const winrt::hstring> args);
|
||||
|
||||
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs);
|
||||
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme);
|
||||
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
|
||||
|
||||
private:
|
||||
bool _isUwp{ false };
|
||||
bool _isElevated{ false };
|
||||
bool _canDragDrop{ false };
|
||||
std::atomic<bool> _notifyRootInitializedCalled{ false };
|
||||
|
||||
// If you add controls here, but forget to null them either here or in
|
||||
// the ctor, you're going to have a bad time. It'll mysteriously fail to
|
||||
// activate the AppLogic.
|
||||
// ALSO: If you add any UIElements as roots here, make sure they're
|
||||
// updated in _ApplyTheme. The root currently is _root.
|
||||
winrt::com_ptr<TerminalPage> _root{ nullptr };
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
|
||||
winrt::hstring _settingsLoadExceptionText;
|
||||
HRESULT _settingsLoadedResult = S_OK;
|
||||
bool _loadedInitialSettings = false;
|
||||
|
||||
bool _hasSettingsStartupActions{ false };
|
||||
uint64_t _numOpenWindows{ 0 };
|
||||
|
||||
std::shared_mutex _dialogLock;
|
||||
winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog;
|
||||
|
||||
::TerminalApp::AppCommandlineArgs _appArgs;
|
||||
::TerminalApp::AppCommandlineArgs _settingsAppArgs;
|
||||
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _reloadSettings;
|
||||
til::throttled_func_trailing<> _reloadState;
|
||||
|
||||
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> _warnings{};
|
||||
|
||||
// These fields invoke _reloadSettings and must be destroyed before _reloadSettings.
|
||||
// (C++ destroys members in reverse-declaration-order.)
|
||||
winrt::com_ptr<LanguageProfileNotifier> _languageProfileNotifier;
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
TerminalApp::ContentManager _contentManager{ winrt::make<implementation::ContentManager>() };
|
||||
|
||||
static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view<const hstring> args,
|
||||
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior);
|
||||
|
||||
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult);
|
||||
void _ShowLoadWarningsDialog();
|
||||
bool _IsKeyboardServiceEnabled();
|
||||
|
||||
void _ApplyLanguageSettingChange() noexcept;
|
||||
void _RefreshThemeRoutine();
|
||||
fire_and_forget _ApplyStartupTaskStateChange();
|
||||
|
||||
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
[[nodiscard]] HRESULT _TryLoadSettings() noexcept;
|
||||
void _ProcessLazySettingsChanges();
|
||||
void _RegisterSettingsChange();
|
||||
fire_and_forget _DispatchReloadSettings();
|
||||
void _OpenSettingsUI();
|
||||
|
||||
bool _hasCommandLineArguments{ false };
|
||||
bool _hasSettingsStartupActions{ false };
|
||||
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> _warnings;
|
||||
|
||||
// These are events that are handled by the TerminalPage, but are
|
||||
// exposed through the AppLogic. This macro is used to forward the event
|
||||
// directly to them.
|
||||
FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent);
|
||||
FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged);
|
||||
FORWARDED_TYPED_EVENT(LastTabClosed, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs, _root, LastTabClosed);
|
||||
FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged);
|
||||
FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged);
|
||||
FORWARDED_TYPED_EVENT(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested);
|
||||
FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged);
|
||||
FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell);
|
||||
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
|
||||
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
|
||||
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
|
||||
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
|
||||
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
|
||||
FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested);
|
||||
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
|
||||
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
|
||||
FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged);
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TerminalAppLocalTests::CommandlineTest;
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
import "TerminalWindow.idl";
|
||||
|
||||
import "TerminalPage.idl";
|
||||
import "ShortcutActionDispatch.idl";
|
||||
import "IDirectKeyListener.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
struct InitialPosition
|
||||
{
|
||||
Int64 X;
|
||||
Int64 Y;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass FindTargetWindowResult
|
||||
{
|
||||
Int32 WindowId { get; };
|
||||
String WindowName { get; };
|
||||
};
|
||||
|
||||
struct ParseCommandlineResult
|
||||
delegate void SystemMenuItemHandler();
|
||||
|
||||
enum SystemMenuChangeAction
|
||||
{
|
||||
String Message;
|
||||
Int32 ExitCode;
|
||||
Add = 0,
|
||||
Remove = 1
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SystemMenuChangeArgs {
|
||||
String Name { get; };
|
||||
SystemMenuChangeAction Action { get; };
|
||||
SystemMenuItemHandler Handler { get; };
|
||||
};
|
||||
|
||||
// See IDialogPresenter and TerminalPage's DialogPresenter for more
|
||||
// information.
|
||||
[default_interface] runtimeclass AppLogic
|
||||
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
AppLogic();
|
||||
|
||||
@@ -29,33 +46,98 @@ namespace TerminalApp
|
||||
// registered?" when it definitely is.
|
||||
void Create();
|
||||
|
||||
Boolean IsRunningElevated();
|
||||
Boolean CanDragDrop();
|
||||
|
||||
ContentManager ContentManager { get; };
|
||||
Boolean IsUwp();
|
||||
void RunAsUwp();
|
||||
Boolean IsElevated();
|
||||
|
||||
Boolean HasCommandlineArguments();
|
||||
Boolean HasSettingsStartupActions();
|
||||
Int32 SetStartupCommandline(String[] commands);
|
||||
Int32 ExecuteCommandline(String[] commands, String cwd);
|
||||
String ParseCommandlineMessage { get; };
|
||||
Boolean ShouldExitEarly { get; };
|
||||
|
||||
Boolean ShouldUsePersistedLayout();
|
||||
void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector<String> layouts);
|
||||
void Quit();
|
||||
|
||||
void ReloadSettings();
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
|
||||
void SetInboundListener();
|
||||
|
||||
String Title { get; };
|
||||
|
||||
Boolean FocusMode { get; };
|
||||
Boolean Fullscreen { get; };
|
||||
void Maximized(Boolean newMaximized);
|
||||
Boolean AlwaysOnTop { get; };
|
||||
Boolean AutoHideWindow { get; };
|
||||
|
||||
void IdentifyWindow();
|
||||
String WindowName;
|
||||
UInt64 WindowId;
|
||||
void SetPersistedLayoutIdx(UInt32 idx);
|
||||
void SetNumberOfOpenWindows(UInt64 num);
|
||||
void RenameFailed();
|
||||
void RequestExitFullscreen();
|
||||
Boolean IsQuakeWindow();
|
||||
|
||||
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
|
||||
Boolean CenterOnLaunch { get; };
|
||||
|
||||
InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY);
|
||||
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
|
||||
Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode();
|
||||
Boolean GetShowTabsInTitlebar();
|
||||
Boolean GetInitialAlwaysOnTop();
|
||||
Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
|
||||
void TitlebarClicked();
|
||||
void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position);
|
||||
void WindowVisibilityChanged(Boolean showOrHide);
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
|
||||
void WindowActivated(Boolean activated);
|
||||
|
||||
Boolean ShouldUsePersistedLayout();
|
||||
Boolean ShouldImmediatelyHandoffToElevated();
|
||||
void HandoffToElevated();
|
||||
String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position);
|
||||
void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector<String> layouts);
|
||||
|
||||
Boolean GetMinimizeToNotificationArea();
|
||||
Boolean GetAlwaysShowNotificationIcon();
|
||||
Boolean GetShowTitleInTitlebar();
|
||||
|
||||
// Selected settings to expose
|
||||
Microsoft.Terminal.Settings.Model.Theme Theme { get; };
|
||||
Boolean IsolatedMode { get; };
|
||||
Boolean AllowHeadless { get; };
|
||||
Boolean RequestsTrayIcon { get; };
|
||||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
|
||||
TerminalWindow CreateNewWindow();
|
||||
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
|
||||
|
||||
ParseCommandlineResult GetParseCommandlineMessage(String[] args);
|
||||
|
||||
IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, SettingsLoadEventArgs> SettingsChanged;
|
||||
// See IDialogPresenter and TerminalPage's DialogPresenter for more
|
||||
// information.
|
||||
Windows.Foundation.IAsyncOperation<Windows.UI.Xaml.Controls.ContentDialogResult> ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog);
|
||||
void DismissDialog();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Theme> RequestedThemeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ChangeMaximizeRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> RaiseVisualBell;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "pch.h"
|
||||
#include "ColorPickupFlyout.h"
|
||||
#include "ColorPickupFlyout.g.cpp"
|
||||
#include "winrt/Windows.UI.Xaml.Media.h"
|
||||
#include "winrt/Windows.UI.Xaml.Shapes.h"
|
||||
#include "winrt/Windows.UI.Xaml.Interop.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
|
||||
@@ -107,7 +107,8 @@
|
||||
x:Uid="TabColorClearButton"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="ClearColorButton_Click" />
|
||||
Click="ClearColorButton_Click"
|
||||
Content="Reset" />
|
||||
<ToggleButton x:Name="CustomColorButton"
|
||||
x:Uid="TabColorCustomButton"
|
||||
Grid.Column="1"
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "CommandPalette.g.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
|
||||
#include <til/hash.h>
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
@@ -62,14 +60,6 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(PreviewAction, Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::Command);
|
||||
|
||||
private:
|
||||
struct winrt_object_hash
|
||||
{
|
||||
size_t operator()(const auto& value) const noexcept
|
||||
{
|
||||
return til::hash(winrt::get_abi(value));
|
||||
}
|
||||
};
|
||||
|
||||
friend struct CommandPaletteT<CommandPalette>; // for Xaml to bind events
|
||||
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _allCommands{ nullptr };
|
||||
@@ -151,7 +141,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _choosingItemContainer(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs& args);
|
||||
void _containerContentChanging(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ContainerContentChangingEventArgs& args);
|
||||
winrt::TerminalApp::PaletteItemTemplateSelector _itemTemplateSelector{ nullptr };
|
||||
std::unordered_map<Windows::UI::Xaml::DataTemplate, std::unordered_set<Windows::UI::Xaml::Controls::Primitives::SelectorItem, winrt_object_hash>, winrt_object_hash> _listViewItemsCache;
|
||||
std::unordered_map<Windows::UI::Xaml::DataTemplate, std::unordered_set<Windows::UI::Xaml::Controls::Primitives::SelectorItem>> _listViewItemsCache;
|
||||
Windows::UI::Xaml::DataTemplate _listItemTemplate;
|
||||
|
||||
friend class TerminalAppLocalTests::TabTests;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ContentManager.h"
|
||||
#include "ContentManager.g.cpp"
|
||||
|
||||
#include <wil/token_helpers.h>
|
||||
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
ControlInteractivity ContentManager::CreateCore(const Microsoft::Terminal::Control::IControlSettings& settings,
|
||||
const IControlAppearance& unfocusedAppearance,
|
||||
const TerminalConnection::ITerminalConnection& connection)
|
||||
{
|
||||
ControlInteractivity content{ settings, unfocusedAppearance, connection };
|
||||
content.Closed({ get_weak(), &ContentManager::_closedHandler });
|
||||
|
||||
_content.emplace(content.Id(), content);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
ControlInteractivity ContentManager::TryLookupCore(uint64_t id)
|
||||
{
|
||||
const auto it = _content.find(id);
|
||||
return it != _content.end() ? it->second : ControlInteractivity{ nullptr };
|
||||
}
|
||||
|
||||
void ContentManager::Detach(const Microsoft::Terminal::Control::TermControl& control)
|
||||
{
|
||||
const auto contentId{ control.ContentId() };
|
||||
if (const auto& content{ TryLookupCore(contentId) })
|
||||
{
|
||||
control.Detach();
|
||||
}
|
||||
}
|
||||
|
||||
void ContentManager::_closedHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable&)
|
||||
{
|
||||
if (const auto& content{ sender.try_as<winrt::Microsoft::Terminal::Control::ControlInteractivity>() })
|
||||
{
|
||||
const auto& contentId{ content.Id() };
|
||||
_content.erase(contentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user